728x90

판다스를 이용하면 대용량의 데이터도 쉽게 읽어서 분석할 수 있다.

 

 

https://www.data.go.kr/ 에서  샘플 데이터 가져오기

 

 

 

다운로드 받은 파일이 압축파일이며, 내용을 보면 아래와 같이  csv 파일로 되어 있다.

서울 파일을 영문이름으로 변경했다.

 

이제, 파이참(PyCharm)에서 코드를 실행해 본다.

https://www.w3schools.com/python/pandas/default.asp 에 기본적인 코드 사용법이 잘 나와있다.

 

 

 

import pandas as pd
 
# data = pd.read_csv('soho_seoul.csv', header=None) # KeyError: '상권업종대분류명'
df = pd.read_csv('soho_seoul.csv'# KeyError: '상권업종대분류명'
 
## Fastest would be using length of index
print("전체 행 : "len(df.index))
 
## If you want the column and row count then
row_count, column_count = df.shape
print("총 행(row) 수 : ", row_count)
print("총 칼럼(열) 수 : ", column_count)
 
# 데이터 셋 df의 종합적인 정보는 df.info( ) 함수를 통해 확인 가능
# Dtype의 int64는 정수, object는 문자열, float64는 실수를 의미
print(df.info())
 
# 데이터 프레임에서 결측치를 True, 값이 있으면 False를 반환
print(df.isnull())
 
# 각 column들이 몇개의 null값을 가졌는지 확인
is_null = df.isnull().sum()
print(is_null)
 

 

jupyter notebook 에서 df.info() 를 한 결과 화면이다.

 

 

열의 칼럼이 39개나 되어서 출력 결과가 보기 좋지 않아 칼럼 일부만 발췌해서 출력하는 걸 테스트했다.

 

# 위에서부터 지정된 개수만큼 출력하기
df_head = df.head(10# 개수를 지정하지 않으면 기본 5개 출력
print(df_head)
 
# 3 ~ 6번째 데이터 출력, 칼럼(열)은 10번째 열까지 출력
print(df.iloc[2:7,:10])
 
# 열 여러개 선택하기
new_df = df.head(20)[['상호명','상권업종대분류명','상권업종중분류명','상권업종소분류명','표준산업분류명','시군구명','행정동명','건물명']]
print(new_df)
 
# 맨 하단 10개 데이터를 칼럼(열) 10개만 출력
print(df.tail(10).iloc[:,:10])
 
print(df.tail(10)[['상가업소번호','상호명','상권업종대분류명','상권업종중분류명','상권업종소분류명','표준산업분류명','시군구명','행정동명','건물명']])
 
 

 

 

특정 칼럼 중복 제거 및 DB에 데이터 저장

# 특정 칼럼 중복 제거
item = df['상권업종대분류명'].drop_duplicates()
print(item)
 
# LIST로 변환
item_list = item.values.tolist()
print(item_list)
 
# 가나다순 정렬
varlist = sorted(item_list)
print(varlist)
 
# LIST to DB Insert
 

 

 

판다스에서 읽은 데이터를 mariaDB에 저장 테스트 목적으로 샘플 테이블을 생성했다.

다중 카테고리로 사용하는 테이블이다.

CREATE TABLE cate (
  `id` int(11NOT NULL,
  `parent_id` int(11NOT NULL DEFAULT 0,
  `depth` int(3NOT NULL DEFAULT 1,
  `name` varchar(50NOT NULL,
  `text` varchar(50DEFAULT NULL
ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
ALTER TABLE cate
  ADD PRIMARY KEY (`id`);
 
ALTER TABLE cate
  MODIFY `id` int(11NOT NULL AUTO_INCREMENT;
COMMIT;
 

 

데이터 무결성(중복 저장 방지)을 위해서 다중칼럼 UNIQUE 인덱스를 추가했다.

parent_id, depth, name 칼럼이 마치 1개의 칼럼처럼 동작하여 동일한 자료가 업로드되면 추가되지 않는다.

보통 userID 가 중복되지 않도록 1개의 칼럼에 UNIQUE 인덱스를 설정하는데, 데이터가 중복 저장되지 않도록 하기 위한 모든 칼럼에 다중 UNIQUE INDEX를 설정하면 중복 저장을 방지할 수 있다.

 

ALTER TABLE cate
  ADD UNIQUE KEY parentid_name (parent_id,depth,name);
 

 

DB에 데이터 Insert 과정의 소스 코드

csv 파일의 데이터를 읽어서 원하는 칼럼의 자료를 선택하고 중복된 값을 제거하고 DB에 카테고리화를 위한 데이터 저장 목적으로 코드를 연습해 본 것이다.

# pip install PyMySQL # mysql 연동시
# pip install mariadb
# pip install numpy scipy matplotlib ipython scikit-learn pandas pillow imageio
 
import pandas as pd
import mariadb
import sys
 
# Connect to MariaDB Platform
try:
    mydb = mariadb.connect(
        user="root",
        password="autoset",
        host="localhost",
        port=3306,
        database="python_sample"
 
    )
except mariadb.Error as e:
    print(f"Error connecting to MariaDB Platform: {e}")
    sys.exit(1)
 
# Get Cursor
dbconn = mydb.cursor()
 
 
# data = pd.read_csv('soho_seoul.csv', header=None) # KeyError: '상권업종대분류명'
df = pd.read_csv('soho_seoul.csv') # KeyError: '상권업종대분류명'
 
## Fastest would be using length of index
print("전체 행 : ", len(df.index))
 
## If you want the column and row count then
row_count, column_count = df.shape
print("총 행(row) 수 : ", row_count)
print("총 칼럼(열) 수 : ", column_count)
 
# 특정 칼럼 중복 제거
item = df['상권업종대분류명'].drop_duplicates()
print(item)
 
# LIST로 변환
item_list = item.values.tolist()
print(item_list)
 
# 가나다순 정렬
varlist = sorted(item_list)
print(varlist)
 
# 배열 사이즈 구하기
print(len(varlist))
 
# 배열 사이즈만큼 동일한 값 초기화, 두가지 방법 모두 가능
parent_id = [0 for i in range(len(varlist))]
depth = [1* len(varlist)
 
# Series 생성 및 DataFrame 전환 <== mariaDB 입력 데이터 생성 목적
df_parentid = pd.Series(parent_id)
df_depth = pd.Series(depth)
df_name = pd.Series(varlist)
 
df_all = pd.concat([df_parentid, df_depth, df_name, df_name], axis=1)
print(df_all)
 
# DataFrame to List
db_itemlist = df_all.values.tolist()
print(db_itemlist)
 
# LIST to mariaDB Insert
sql = "INSERT INTO cate(parent_id,depth,name,text) VALUES(%s,%s,%s,%s)"
dbconn.executemany(sql,db_itemlist)
 
 

 

 

DB에 잘 저장되었는지 여부를 확인해 보자.

 

 

파일을 다시 한번 더 실행하면 아래와 같은 에러 메시지를 출력한다.

 

위와 같은 메시지가 출력되는 걸 방지하고 싶다면, INSERT IGNORE INTO 로 변경하면 된다.

중복하려는 자료가 있다면 기존 자료를 유지하고, 새로운 자료는 무시하라는 명령어이다.

 

# LIST to mariaDB Insert
sql = "INSERT IGNORE INTO cate(parent_id,depth,name,text) VALUES(%s,%s,%s,%s)"
dbconn.executemany(sql,db_itemlist)
dbconn.close()
 

 

 

'파이썬 > 데이터 분석' 카테고리의 다른 글

MariaDB to Python Pandas DataFrame  (0) 2022.01.21
Python Pandas 기초 학습  (0) 2022.01.18
Pycharm과 Jupyter Notebook 연결하기  (0) 2022.01.12
블로그 이미지

Link2Me

,
728x90

pandas란 엑셀처럼 테이블(표)로 된 데이터를 다룰 때 아주 유용한 도구이다.

판다스는 데이터 분석용 언어인 R의 data.frame 구조를 본뜬 DataFrame이라는 구조를 사용하기 때문에, R의 data.frame의 기능들을 대부분 사용할 수 있도록 만들어졌다.

index 를 제외한 각 열(Column)을 Series라고 부른다.

각 열 단위(Series)가 모여 하나의 표를 DataFrame 이라고 부른다.

 

 

아래 코드는 jupyter notebook 환경에서 실습하면 된다.

https://link2me.tistory.com/2094  게시글에 라이브러리 설치와 Jupyter notebook 설치하는 방법이 기술되어 있다.

 

index 를 내가 원하는 형식으로 변경할 수 있다.

import pandas as pd
# Series 는 인덱스와 값으로 구성된다.
 
array = pd.Series(['사과''바나나''당근'], index=['a','b','c'])
 
print(array)
print()
 
# index 출력하기
for i in array.index:
    print(i)
    
print()
 
# value 출력하기
for val in array.values:
    print(val)

 

import pandas as pd
data = {
    'a''사과',
    'b''바나나',
    'c''당근'
}
 
# Dict 자료형을 Series로 바꾸기
array = pd.Series(data)
 
print(array)
 

 

import pandas as pd
 
# 데이터 프레임 : 다수의 시리즈(Series)를 모아 처리하기 위한 목적으로 사용
# Dict 자료형
word_dict = {
    'Apple''사과',
    'Banana''바나나',
    'Carrot''당근'
}
 
frequency_dict = {
    'Apple'3,
    'Banana'5,
    'Carrot'7
}
 
word = pd.Series(word_dict)
frequency = pd.Series(frequency_dict)
 
# name : value
summary = pd.DataFrame({
    'word': word,
    'frequency': frequency
})
 
display(summary)
 
 

 

import pandas as pd
 
# Series 끼리 사칙연산이 가능하다. 사칙연산을 한 결과는 또 다른 Series이다.
 
word_dict = {
    'Apple''사과',
    'Banana''바나나',
    'Carrot''당근'
}
 
frequency_dict = {
    'Apple'3,
    'Banana'5,
    'Carrot'7
}
 
importance_dict = {
    'Apple'3,
    'Banana'2,
    'Carrot'1
}
 
word = pd.Series(word_dict)
frequency = pd.Series(frequency_dict)
importance = pd.Series(importance_dict)
 
summary = pd.DataFrame({
    'word': word,
    'frequency': frequency,
    'importance': importance
})
 
 
score = summary['frequency'* summary['importance']
summary['score'= score
 
display(summary)
 

 

DataFrame 의  칼럼(열) 순서를 바꿔보고 싶다면....

import pandas as pd
 
# 데이터 프레임의 슬라이싱
 
word_dict = {
    'Apple''사과',
    'Banana''바나나',
    'Carrot''당근',
    'Durian''두리안'
}
 
frequency_dict = {
    'Apple'3,
    'Banana'5,
    'Carrot'7,
    'Durian'2
}
 
importance_dict = {
    'Apple'3,
    'Banana'2,
    'Carrot'1,
    'Durian'1
}
 
word = pd.Series(word_dict)
frequency = pd.Series(frequency_dict)
importance = pd.Series(importance_dict)
 
summary = pd.DataFrame({
    'word': word,
    'frequency': frequency,
    'importance': importance
})
 
display(summary)
 
# DataFrame 의 칼럼(열) 순서를 바꿔보기
df = pd.DataFrame(summary, columns=['word''importance''frequency'])
display(df)
 
# 이름을 기준으로 슬라이싱
display(summary.loc['Banana':'Carrot''importance':])
 
 
# 인덱스를 기준으로 슬라이싱
display(summary.iloc[1:32:])
 

 

 

# 데이터 프레임의 연산
import pandas as pd
 
word_dict = {
    'Apple''사과',
    'Banana''바나나',
    'Carrot''당근',
    'Durian''두리안'
}
 
frequency_dict = {
    'Apple'3,
    'Banana'5,
    'Carrot'7,
    'Durian'2
}
 
importance_dict = {
    'Apple'3,
    'Banana'2,
    'Carrot'1,
    'Durian'1
}
 
word = pd.Series(word_dict)
frequency = pd.Series(frequency_dict)
importance = pd.Series(importance_dict)
 
summary = pd.DataFrame({
    'word': word,
    'frequency': frequency,
    'importance': importance
})
 
display(summary)
 
summary.loc['Apple''importance'= 9 # 데이터의 변경
summary.loc['Elderberry'= ['엘더베리'53# 새 데이터 삽입
 
display(summary)
 
print()
# 끝에서 마지막 2줄을 불러온다. tail() 로 하면 마지막 5개를 불러온다.
display(summary.tail(2))
 
print()
# 맨 처음부터 2줄을 불러온다.
display(summary.head(2))
 
# 데이터의 대략적인 통계적 정보 요약을 보여준다.
display(summary.describe())
 
 

 

# 엑셀로 내보내기/불러오기
import pandas as pd
 
word_dict = {
    'Apple''사과',
    'Banana''바나나',
    'Carrot''당근'
}
 
frequency_dict = {
    'Apple'3,
    'Banana'5,
    'Carrot'7
}
 
word = pd.Series(word_dict)
frequency = pd.Series(frequency_dict)
 
summary = pd.DataFrame({
    'word': word,
    'frequency': frequency
})
 
 
summary.to_csv("summary.csv", encoding="utf-8-sig")
# csv 파일 형태로 저장한 데이터는 텍스트 파일 형태
 
# csv 파일 엑셀 읽어오기 및 화면 출력
saved = pd.read_csv("summary.csv", index_col=0)
display(saved)
 
 

 

 

'파이썬 > 데이터 분석' 카테고리의 다른 글

MariaDB to Python Pandas DataFrame  (0) 2022.01.21
Python Pandas CSV 읽고 DB 저장  (0) 2022.01.19
Pycharm과 Jupyter Notebook 연결하기  (0) 2022.01.12
블로그 이미지

Link2Me

,
728x90

장고 온라인 강좌 로그인 처리가 매끄럽지 못한 거 같아서 jQuery ajax 로그인 처리 기능을 테스트하고 적어둔다.

 

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>로그인</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div class="container pt-5">
    <div class="login-form">
        <div class="row">
            <div class="col-12">
                {{ error }}
            </div>
        </div>
        <form method="post" id="post-form">
            {% csrf_token %}
            <h2 class="text-center">로그인</h2>
            <div class="form-group">
                <input type="text" id="useremail" class="form-control" placeholder="user email 입력">
            </div>
            <div class="form-group">
                <input type="password" id="password" class="form-control" placeholder="비밀번호 입력">
            </div>
            <div class="form-group">
                <button type="submit" class="btn btn-primary btn-block">로그인</button>
            </div>
        </form>
        <p class="text-center"><a href="#" id="register">회원 가입</a></p>
    </div>
</div>
</body>
<script>
    $(document).on('submit''#post-form'function (e) {
        e.preventDefault();
        if($('#useremail').val() == ''){
            alert('email을 입력하세요');
            $('#useremail').focus();
            return false;
        }
 
        if($('#password').val() == ''){
            alert('비밀번호를 입력하세요');
            $('#password').focus();
            return false;
        }
 
        $.ajax({
            type: 'POST',
            url: '{% url "user:ajax_login" %}',
            data: {
                useremail: $('#useremail').val(),
                password: $('#password').val(),
                csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
                action: 'post'
            },
            success: function (json) {
                if(json.status == 1){
                    document.getElementById("post-form").reset();
                    window.location.href = 'http://127.0.0.1:8000/';
                    alert('로그인되었습니다.');
                } else if(json.status == 0){
                    alert('로그인 정보를 확인하세요.');
                }
            },
            error: function (xhr, errmsg, err) {
                alert('에러가 발생했네요.' + errmsg);
                console.log(xhr.status + ": " + xhr.responseText); 
            }
        });
    });
</script>
</html>
 

 

 

views.py

완벽 동작하도록 로직을 수정했지만, Secure Coding까지 고려하지는 않은 상태다.

로그인에서 비밀번호가 일치하지 않거나, 아이디가 없거나 할 경우에 동일한 메시지를 출력해야 한다.

 

 
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import make_password, check_password
from .models import User
 
# Create your views here.
def home(reqest):
    user_id = reqest.session.get('user')
 
    if user_id:
        user = User.objects.get(pk=user_id)
        return HttpResponse(user.useremail)
 
    return HttpResponse('Home')
 
 
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        useremail = request.POST.get('useremail'None)
        password = request.POST.get('password'None)
 
        res_data = {}
        try:
            user = User.objects.get(useremail=useremail)
            if check_password(password, user.password):
                # 비밀번호가 일치하면 세션 생성
                request.session['user'= user.id
                res_data['status'= '1'
            else:
                res_data['status'= '0'
            return JsonResponse(res_data)
        except User.DoesNotExist:
            # 대문자 User 임에 주의
            res_data['status'= '0'
            return JsonResponse(res_data)
 
        return render(request, 'login.html', res_data)
 
 
def logout(request):
    if request.session.get('user'):
        del(request.session['user'])
 
    return redirect('/user/login')
 
 

 

 

urls.py

from django.urls import path
from . import views
 
app_name = 'user'
urlpatterns = [
    path('register/', views.register, name='ajax_register'),
    path('login/', views.login, name='ajax_login'),
    path('logout/', views.logout),
]
 
 

 

참고자료

https://dev.to/thepylot/how-to-send-django-form-with-ajax-4bpo

 

How to send Django form with AJAX

What's up DEV Network? In this quick tutorial I am going to show you how to POST Django form without...

dev.to

 

블로그 이미지

Link2Me

,
728x90

장고 로그인 기능 기본적인 예제이다.

 

# models.py
from django.db import models
 
class User(models.Model):
    username = models.CharField(max_length=32, verbose_name='사용자명',null=True,default='')
    useremail = models.EmailField(max_length=128, verbose_name='emailID',default='')
    password = models.CharField(max_length=128,verbose_name='비밀번호')
    registered_dttm = models.DateTimeField(auto_now_add=True, verbose_name='등록시간')
 
    def __str__(self):
        return self.email
 
    class Meta:
        db_table = 'django_user'
        verbose_name = '사용자'
        verbose_name_plural = '사용자'
 
# admin.py
from django.contrib import admin
from .models import User
 
class UserAdmin(admin.ModelAdmin):
    list_display = ('username','useremail','password','registered_dttm')
 
admin.site.register(User,UserAdmin)
 
 
# views.py
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import make_password, check_password
from .models import User
 
def home(reqest):
    user_id = reqest.session.get('user')
 
    if user_id:
        user = User.objects.get(pk=user_id)
        return HttpResponse(user.useremail)
 
    return HttpResponse('Home')
 
 
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        useremail = request.POST.get('useremail'None)
        password = request.POST.get('password'None)
 
        res_data = {}
        if not (useremail and password):
            res_data['error'= '모든 값을 입력해야 합니다.'
        else:
            user = User.objects.get(useremail=useremail)
            if check_password(password,user.password):
                request.session['user'= user.id
                return redirect('/')
            else:
                res_data['error'= '로그인 정보를 확인하세요'
 
        return render(request, 'login.html', res_data)
 
 
def register(request):
    if request.method == 'GET':
        return render(request, 'register.html')
    elif request.method == 'POST':
        username = request.POST.get('username',None)
        useremail = request.POST.get('useremail',None)
        password = request.POST.get('password',None)
        repasswd = request.POST.get('repasswd',None)
 
        res_data ={}
        if not (username and useremail and password and repasswd):
            res_data['error'= '모든 값을 입력해야 합니다.'
        elif password != repasswd:
            res_data['error'='비밀번호가 다릅니다.'
        else:
            user = User(
                username = username,
                useremail = useremail,
                password = make_password(password)
            )
            user.save()
 
        return render(request,'register.html',res_data)
 
 
def logout(request):
    if request.session.get('user'):
        del(request.session['user'])
 
    return redirect('/')
 
 
 
# urls.py
from django.urls import path
from . import views
 
app_name = 'accounts'
urlpatterns = [
    path('register/', views.register, name='ajax_register'), 
    path('login/', views.login),
    path('logout/',views.logout),
]
 
 

 

 

templates 디렉토리 안에 있는 login.html 파일에서

form 태그 안에 {% csrf_token %} 부분이 백엔드 장고와 통신하는 명령어이다.

jQuery ajax 처리하는 걸 시도했으나 아직 성공하지 못했다.

 
<!DOCTYPE html>
<html lang="en">
<head>
    <title>로그인</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        .login-form {
            width: 340px;
            margin: 50px auto;
            font-size: 15px;
        }
 
        .login-form form {
            margin-bottom: 15px;
            background: #f7f7f7;
            box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
            padding: 30px;
        }
 
        .login-form h2 {
            margin: 0 0 15px;
        }
 
        .form-control, .btn {
            min-height: 38px;
            border-radius: 2px;
        }
 
        .btn {
            font-size: 15px;
            font-weight: bold;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="login-form">
        <div class="row">
            <div class="col-12">
                {{ error }}
            </div>
        </div>
        <form method="post" action=".">
            {% csrf_token %}
            <h2 class="text-center">로그인</h2>
            <div class="form-group">
                <input type="text" name="useremail" class="form-control" placeholder="user email"
                       required="required">
            </div>
            <div class="form-group">
                <input type="password" name="password" class="form-control" placeholder="Password"
                       required="required">
            </div>
            <div class="form-group">
                <button type="submit" class="btn btn-primary btn-block">로그인</button>
            </div>
        </form>
        <p class="text-center"><a href="#" id="register">회원 가입</a></p>
    </div>
</div>
</body>
</html>

 

 

templates 디렉토리 안에 있는 register.html 파일이다.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>회원가입</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        body {
            color: #fff;
            background: #63738a;
            font-family: 'Roboto', sans-serif;
        }
 
        .form-control {
            height: 40px;
            box-shadow: none;
            color: #969fa4;
        }
 
        .form-control:focus {
            border-color: #5cb85c;
        }
 
        .form-control, .btn {
            border-radius: 3px;
        }
 
        .signup-form {
            width: 450px;
            margin: 0 auto;
            padding: 30px 0;
            font-size: 15px;
        }
 
        .signup-form h2 {
            color: #636363;
            margin: 0 0 15px;
            position: relative;
            text-align: center;
        }
 
        .signup-form h2:before, .signup-form h2:after {
            content: "";
            height: 2px;
            width: 30%;
            background: #d4d4d4;
            position: absolute;
            top: 50%;
            z-index: 2;
        }
 
        .signup-form h2:before {
            left: 0;
        }
 
        .signup-form h2:after {
            right: 0;
        }
 
        .signup-form .hint-text {
            color: #999;
            margin-bottom: 30px;
            text-align: center;
        }
 
        .signup-form form {
            color: #999;
            border-radius: 3px;
            margin-bottom: 15px;
            background: #f2f3f7;
            box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
            padding: 30px;
        }
 
        .signup-form .form-group {
            margin-bottom: 20px;
        }
 
        .signup-form input[type="checkbox"] {
            margin-top: 3px;
        }
 
        .signup-form .btn {
            font-size: 16px;
            font-weight: bold;
            min-width: 140px;
            outline: none !important;
        }
 
        .signup-form .row div:first-child {
            padding-right: 10px;
        }
 
        .signup-form .row div:last-child {
            padding-left: 10px;
        }
 
        .signup-form a {
            color: #fff;
            text-decoration: underline;
        }
 
        .signup-form a:hover {
            text-decoration: none;
        }
 
        .signup-form form a {
            color: #5cb85c;
            text-decoration: none;
        }
 
        .signup-form form a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="signup-form">
        <div class="row">
            <div class="col-12">
                {{ error }}
            </div>
        </div>
        <form id="form" method="post" action=".">
            {% csrf_token %}
            <h2>회원가입</h2>
            <div class="form-group">
                <div class="row">
                    <div class="col">
                        <input type="text" class="form-control" name="username" placeholder="사용자 이름"
                               required="required"/>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <input type="email" class="form-control" name="useremail" placeholder="Email" required="required"/>
            </div>
            <div class="form-group">
                <input type="password" class="form-control" name="password" placeholder="비밀번호" required="required">
            </div>
            <div class="form-group">
                <input type="password" class="form-control" name="repasswd" placeholder="비밀번호 확인"
                       required="required">
            </div>
            <div class="form-group">
                <button type="submit" class="btn btn-success btn-lg btn-block">회원 등록</button>
            </div>
        </form>
        <div class="text-center"><a href="#">로그인</a></div>
    </div>
</div>
<script>
 
</script>
</body>
</html>
 

 

 

 

블로그 이미지

Link2Me

,
728x90

Pycharm 에서 Jupyter Notebook 연결하는 방법을 적어둔다.

 

프로젝트 생성

https://link2me.tistory.com/2090 를 참조하면 도움될 것이다.

 

Jupyter Notebook 실행시 실행이 안되는 경우가 있어서 업그레이드를 해준 사항

 

Jupyter Notebook 연결

Jupyter notebook은 프로그램 코드를 브라우저에서 실행해주는 대화식 환경이다.

Jupyter notebook 을 설치하는 방법은 UI 화면에서 하는 방법과 터미널 창에서 하는 방법이 있다.

터미널 창에서 아래 명령어를 실행한다.

# pip install numpy scipy matplotlib ipython scikit-learn pandas pillow imageio
# pip install graphviz
# pip install nltk spacy
# pip install tensorflow
# pip install jupyter notebook
 

 

아래 그림에서 + 버튼을 눌러서 jupyter 를 입력하고 Install Packages 를 눌러서 설치해도 된다.

 

 

위 그림과 같이 설정하고 하는 방법도 있지만....

그냥 터미널 창에서 실행하면 자동으로 Web 창에서 실행할 ULR 이 나온다.

 

jupyter notebook 을 입력하고 엔터키를 치면

 

아래와 같은 웹화면에서 데이터를 입력하고 Run 버튼을 클릭하면 3번과 같은 결과가 출력된다.

pandas란 엑셀처럼 테이블(표)로 된 데이터를 다룰 때 아주 유용한 도구이다.

판다스는 데이터 분석용 언어인 R의 data.frame 구조를 본뜬 DataFrame이라는 구조를 사용하기 때문에, R의 data.frame의 기능들을 대부분 사용할 수 있도록 만들어졌다.

 

 

 

이제 Github 에 올려진 파일을 받아서 보면서 연습 실행해보면 도움된다.

https://github.com/rickiepark/introduction_to_ml_with_python

 

GitHub - rickiepark/introduction_to_ml_with_python: 도서 "[개정판] 파이썬 라이브러리를 활용한 머신 러닝"의

도서 "[개정판] 파이썬 라이브러리를 활용한 머신 러닝"의 주피터 노트북과 코드입니다. Contribute to rickiepark/introduction_to_ml_with_python development by creating an account on GitHub.

github.com

 

자세한 설명을 원하면 책을 구입해서 봐야 한다.

 

Github 자료를 다운로드 하는 법

 

git clone 하고 2번 복사한 것을 붙여넣기 하면 아래와 같이 된다.

 

 

'파이썬 > 데이터 분석' 카테고리의 다른 글

MariaDB to Python Pandas DataFrame  (0) 2022.01.21
Python Pandas CSV 읽고 DB 저장  (0) 2022.01.19
Python Pandas 기초 학습  (0) 2022.01.18
블로그 이미지

Link2Me

,
728x90

파이참(PyCharm)을 이용해서 장고 프로젝트를 생성하는 과정을 적어둔다.

 

 

디렉토리를 정해두면 프로젝트명은 자동으로 생성되도록 나온다. 변경을 하던지 그대로 하면 된다.

Virtual 환경 설정 폴더는 별도로 지정하였다.

 

커맨드 창에서

python manage.py startproject djangoProject1

이라고 생성하는 것과 동일하게 자동으로 아래와 같이 프로젝트가 생성된 걸 확인할 수 있다.

 

애플리케이션 생성

startapp 을 생성하는 과정이다.

python manage.py startapp blog

python manage.py startapp accounts

웹 사이트에 대한 전체 프로그램을 프로젝트라고 하고, 모듈화된 단위 프로그램을 애플리케이션이라고 부른다.

 

그러면 장고(Django)가 accounts 디렉토리와 그 하위에 필요한 파일들을 생성해 준다.

blog 디렉토리와 그 하위에 필요한 파일들을 생성해 준다.

 

settings.py 파일 INSTALLED_APPS 의 아래에 두줄을 등록한다.

DEBUG = True
 
ALLOWED_HOSTS = ['*']
 
# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
 
    'accounts.apps.AccountsConfig',
    'blog.apps.BlogConfig',
]

 

DB는 나중에 Maria DB나 MySQL DB를 사용할 경우를 대비하여 아래와 같이 몇줄 주석으로 처리해두었다.

# Database
 
DATABASES = {
    'default': {
        'ENGINE''django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
        # 'ENGINE': 'django.db.backends.mysql', # mysqlclient librarly 설치
        # 'NAME': 'pythondb',
        # 'USER': 'root',
        # 'PASSWORD': 'autoset', # mariaDB 설치 시 입력한 root 비밀번호 입력
        # 'HOST': 'localhost',
        # 'PORT': ''
    }
}

 

static 디렉토리를 추가하는 과정이다.

 

 

settings.py 하단 수정사항

LANGUAGE_CODE = 'en-us'
# 변경
TIME_ZONE = 'Asia/Seoul'
 
USE_I18N = True
# 변경
USE_TZ = False
 
# Static files (CSS, JavaScript, Images)
 
STATIC_URL = 'static/'
# static 디렉토리 추가
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
 

 

기본 테이블 생성

터미널 창을 열어

python manage.py migrate

를 해준다.

migration이란 테이블 및 필드의 생성, 삭제, 변경 등과 같이 데이터베이스에 대한 변경 사항을 알려주는 정보이다.

 

Superuser 생성

admin 사이트에 로그인하기 위한 관리자(superuser)를 생성한다.

  • manage.py : 각종 장고 명령을 수행
  • __init__.py : 모든 Python 패키지에 __init__.py를 둔다. 패키지를 임포트 할 때의 임포트 대상
  • urls.py : 최상위 URL 설정. 앱 내에 별도 urls.py 를 추가하여 사용
  • wsgi.py : 실 서비스에서의 웹 서비스 진입점

여기까지가 프로젝트 생성의 1단계 작업이다.

 

장고에서 Model은 데이터베이스에 액세스하는 컴포넌트이다.

  - 장고의 ORM(Object Relation Mapping)은 DB와 모델을 연결시키는 다리 같은 역할을 한다.

  - 다양한 DB를 지원하고, SQL 문장을 사용하지 않고도 테이블을 조작할 수 있다.

View는 데이터를 가져오고 변형하는 컴포넌트이다.

Template은 데이터를 사용자에게 보여주는 컴포넌트이다.

흔히 장고를 MVT 프레임워크라고 부른다.

 

장고 설치 디렉토리 확인 방법

python -c "import django; print(django.__path__)"

장고 가상환경 설정 디렉토리 하단에 설치된 것을 확인할 수 있다.

 

model 코딩

- models.py 에 테이블을 정의한다.

- admins.py 에 정의된 테이블이 Admin 화면에 보이게 한다.

- python manage.py makemigrations : DB에 변경이 필요한 사항을 추출한다.

- python manage.py migrate : DB에 변경사항을 반영한다.

- python manage.py runserver : 개발용 Web서버로 현재까지 개발한 사항을 확인한다.

 

 

models.py 파일 코드

# pip install django-taggit
# pip install django-widget-tweaks
# pip install pillow
# pip install pytz
# pip install -U pip setuptools wheel
# pip install pyopenssl ndg-httpsclient pyasn1
 
from django.db import models
from taggit.managers import TaggableManager
 
# 모델 : 테이블 정의
class Post(models.Model):
    # PK(Primary Key)는 클래스에 지정해주지 않아도, 장고는 id라는 칼럼을 자동으로 만들어 준다.
    title = models.CharField(verbose_name='TITLE', max_length=50)
    description = models.CharField('DESCRIPTION', max_length=100, blank=True, help_text='simple description text')
    content = models.TextField('CONTENT')
    create_dt = models.DateTimeField('CREATE DATE', auto_now_add=True)
    modify_dt = models.DateTimeField('MODIFY DATE', auto_now=True)
    tags = TaggableManager(blank=True)
 
    # def __str__(self) 메소드는 객체를 문자열로 표현할 때 사용하는 함수이다.    
    def __str__(self):
        return self.title
 
    def get_absolute_url(self):
        return reversed('blog:post_detail', args=(self.id,))
 
    def get_prev(self):
        return self.get_previous_by_modify_dt()
 
    def get_next(self):
        return self.get_next_by_modify_dt()
 
 

 

admin.py 파일 코드

from django.contrib import admin
from blog.models import Post
 
# models.py 파일을 작성하고 난 이후에 이 코드를 작성한다.
class PostAdmin(admin.ModelAdmin):
    list_display = ("id""title""modify_dt""tag_list")
 
    def get_queryset(self, request):
        return super().get_queryset(request).prefetch_related('tags')
 
    def tag_list(self, obj):
        return u", ".join(o.name for o in obj.tags.all())
 
admin.site.register(Post,PostAdmin)
 
# 이후에 할 일 ==> 터미널 창에서 아래 두줄을 실행한다. 코드가 변경되면 다시 두줄을 실행한다.
# python manage.py makemigrations
# python manage.py migrate
 
# migration이란 테이블 및 필드의 생성, 삭제, 변경 등과 같이
# 데이터베이스에 대한 변경사항을 알려주는 정보
 

 

장고는 웹 요청에 있는 URL을 분석하고, 그 결과로 해당 URL에 매핑된 View를 호출한다.

from django.views.generic import ListView, DetailView
 
from blog.models import Post
 
# View : 애플리케이션의 제어 흐름 및 처리로직을 정의
# 클래스형 뷰를 코딩할 때 가장 먼저 고려할 것은 어떤 제네릭뷰를 사용할 것인가 이다.
# 테이블에서 여러개의 레코드를 가져오는 로직이 필요하면 ListView
# 테이블에서 한개의 레코드를 가져오는 로직이 필요하면 DetailView
class PostLV(ListView):
    model = Post
    # template_name = 'blog/post_list.html'
 
class PostDV(DetailView):
    model = Post
 
 

 

URLconf

클라이언트로부터 요청을 받으면 장고는 가장 먼저 요청에 들어있는 URL을 분석한다.

보통 프로젝트 전체에 URL을 정의하는 프로젝트 URL과 앱마다 정의하는 앱URL 계층으로 나눠서 코딩하는 것이 일반적이다.

 
# 프로젝트 urls.py
from django.contrib import admin
from django.urls import path, include
 
urlpatterns = [
    path('admin/', admin.site.urls),
 
    path('blog/', include('blog.urls')),
]
 
 
# 앱(blog) urls.py
# 파일을 별도 생성하고 아래와 같이 코드를 작성한다.
from django.urls import path
from blog import views
 
app_name = 'blog'
urlpatterns = [
    path('post/list/', views.PostLV.as_view(), name='post_list'),
    path('post/<int:pk>/', views.PostDV.as_view(), name='post_detail'),
]
 
 

 

 

 

 

 

블로그 이미지

Link2Me

,
728x90

오픈소스 테마를 적용하기 前 관리자 화면

라이브러리 설치

python -m pip install django-baton

settings.py 수정

수정 후 화면

최상단과 최하단에 추가

urls.py 파일 수정

 

migrate 해주기

python manage.py makemigrations

python manage.py migrate

 

여기까지하면 테마가 적용된다.

화면 깨짐 현상이 발생하면 CTRL + R 을 눌러서 캐시를 삭제하면 된다.

테마 세부 세팅은 추가로 하면 된다.

블로그 이미지

Link2Me

,
728x90

Django 프레임웍에서 MariaDB 10.4 사용을 위한 연결을 하고 SQLite3 DB에서 MariaDB 로 변경하면서 해줘야 할 사항이다.

 

settings.py 수정사항

DATABASES = {
    'default': {
        #'ENGINE': 'django.db.backends.sqlite3',
        #'NAME': BASE_DIR / 'db.sqlite3',
        'ENGINE''django.db.backends.mysql'# mysqlclient librarly 설치
        'NAME''pythondb',
        'USER''root',
        'PASSWORD''autoset'# mariaDB 설치 시 입력한 root 비밀번호 입력
        'HOST''localhost',
        'PORT'''
    }
}

 

 

pythondb 에 테이블이 생성된 것을 phpMyAdmin 을 이용하여 확인해봤다.

테이블명은 애플리케이션명과 모델 클래스명을 밑줄(_)로 연결하고, 모두 소문자로 표시한다.

14개 테이블이 생성된 것을 확인할 수 있다.

 

블로그 이미지

Link2Me

,
728x90

Python 강좌 듣고 장고 프레임웍 사용 방법을 초보 수준으로 배웠다.

MySQL 또는 MariaDB 를 사용할 경우가 훨씬 많을 거 같아서 기존에 PHP + MariaDB 10.3 이 설치된 환경에서 추가로 Python 과 MariaDB를 사용하려고 수차례 삽질을 한 끝에 확인한 것은 MariaDB 10.3 은 Python Django 프레임웍에서 사용하기가 어려운 줄 알았는데 추가 테스트 결과 yum 설치 순서를 변경하니까 MariaDB 10.3 사용하는데 문제가 없었다.

Python Django 프레임웍은 기본 yum 설치하는 것이 가장 속편하더라.

 

###### SQLite3 DB 설정 #################
cd /root
mkdir sqlite3
cd sqlite3
wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.10.2/1.fc22/x86_64/sqlite-3.10.2-1.fc22.x86_64.rpm
wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.10.2/1.fc22/x86_64/sqlite-devel-3.10.2-1.fc22.x86_64.rpm
sudo yum -y install sqlite-3.10.2-1.fc22.x86_64.rpm sqlite-devel-3.10.2-1.fc22.x86_64.rpm
sqlite3 –version
.quit
# 빠져나오는 것은 .quit

 

Python 에서는 mariadb 를 기본 yum 설치한 것은 제대로 인식하더라.

MariaDB 10.3 설치 상태에서 인식이 안되는 현상이 있어서 계속 확인 중이다.

아래 그림과 같이 yum list installed mariadb* 로 설치 상태를 확인하고 해보시라.

(실패 경우) 아래 그림과 위의 그림에서 maria-devel 이 다르다는 걸 알 수 있다.

 

MariaDB 사용을 위한 Python 설치 스크립트

# 아래 스크립트는 root권한 모드에서 복사해서 그대로 이용할수 있도록 만든 스크립트이다.

#은 root 권한에서는 주석이므로 앞에 주석에 해당되는 걸 없앴다.

 

# Python 모듈을 빌드하려면 개발 도구가 필요

# mariadb-devel 대신에 mysql-devel 로 된 것도 있는데 어떤 걸 해도 상관없다.
yum -y install epel-release
yum -y groupinstall 'Development Tools'
yum -y install yum-utils
yum -y install zip unzip wget mc git net-tools
yum -y install mariadb-devel 

yum -y install gcc gcc-c++ python3 python3-devel openssl openssl-devel
yum -y install zlib zlib-devel libffi-devel

python3 -V
pip3 -V

# pip 설치는 가상환경 만들어서 하는 걸 권장하므로 아래 한줄은 주석처리했다. (이 단계에서 하지 말라는 의미)
# pip3 install --upgrade pip

# 현재 Alias 확인
ls -l /bin/python*

ln -s /bin/pip3.6 /bin/pip

 

##### 파이썬 가상환경 설정 ##################################################
mkdir -p /home/httpd/python/
cd /home/httpd/python/
python3 -m pip install virtualenv
python3 -m pip install --upgrade pip
# 가상환경 이름을 django 로 설정했는데 다른 명칭으로 변경해도 된다.
virtualenv django
source /home/httpd/python/django/bin/activate

pip3 install --upgrade pip setuptools
pip3 install pylint
pip3 install twisted

pip3 install Django
pip3 install djangorestframework
pip3 install mysqlclient

# pip3 install mysqlclient 이 한줄 때문에  엄청난 삽질과 시간낭비를 했다.

 

 

MariaDB 설치

# 기본 설치 명령어이다.

yum -y install mariadb mariadb-server

 

# 기본 yum 설치 대신에 MariaDB 10.4 버전 설치 시

vi /etc/yum.repos.d/MariaDB.repo
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.4/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

sudo yum makecache fast
yum -y install MariaDB-server MariaDB-client

 

######### MariaDB 세팅 #########################################

# 기본 yum 설치이든 MariaDB 10.4 를 설치했던 아래 과정은 동일하다.
# mariadb 부팅 시 자동 시작 설정
systemctl enable mariadb

# mariadb 시작
systemctl start mariadb

# mariadb 상태 확인
systemctl status mariadb

# Maria DB 보안 설정하기
mysql_secure_installation
관리자 비밀번호 지정 --> 이 과정은 유투브에 보면 많이 나오고, 내 블로그 MariaDB 10.3 설치 게시글에 나온다.

vi /etc/my.cnf.d/server.cnf
[mysqld]
collation-server = utf8_general_ci
init-connect='SET NAMES utf8'
character-set-server = utf8


vi /etc/my.cnf.d/mysql-clients.cnf
[mysql]
default-character-set=utf8
[mysqldump]
default-character-set=utf8

# MariaDB 서버 재시작
systemctl restart mariadb

mysql -u root -p
비밀번호
status

 

####### 실제 적용 예제 ######
##########################################
// DB 생성
create database django default character set utf8;
use mysql;
create user codefox@localhost identified by 'wonderfull!#%';
grant all privileges on django.* to codefox@localhost;
flush privileges;

 

quit

 

#######################################################################
# 방화벽 설정
yum -y install firewalld

# 방화벽 데몬 시작
systemctl start firewalld

# 서버 부팅 시 firewalld 데몬 자동 시작 설정
systemctl enable firewalld

firewall-cmd --permanent --add-service=http 
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-service=mysql
firewall-cmd --permanent --zone=public --add-port=3306/tcp

firewall-cmd --permanent --zone=public --add-port=8000/tcp
firewall-cmd --reload
firewall-cmd --list-all

#######################################################################

 

이제 장고 프레임웍에서 MairaDB 사용을 위한 준비는 된 셈이다.

 

MariaDB 10.3 제거 방법

이 CASE는 Python Django 사용을 위해 pip3 install mysqlclient 할 때 에러가 발생 시 하시라.

MariaDB 10.3 에 DB 테이블이 있다면 백업부터 하고 지워야 한다.

아래 스크립트는 root권한 모드에서 복사해서 그대로 이용할수 있도록 만든 스크립트이다. #은 root 권한에서는 주석이므로 앞에 주석에 해당되는 걸 없앴다.

 

systemctl stop mariadb
yum list installed mariadb*
yum -y remove MariaDB-client.x86_64 MariaDB-common.x86_64 

yum -y remove MariaDB-compat.x86_64 MariaDB-server.x86_64
rm -rf /etc/yum.repos.d/MariaDB.repo

cd /var/lib/mysql/
rm -rf ib_logfile0 ib_logfile1 ibdata1

cd /var/lib
rm -rf mysql/

블로그 이미지

Link2Me

,
728x90

결론부터 말하면 Python3.9 소스 설치하는 방법으로 설치는 문제없이 잘되었다.

문제는 SQLite3 DB 인식이 문제가 되어 온갖 삽질을 해도 해결이 잘 안되어서 포기하고.....

CentOS 7.X 버전에 python3 를 yum 으로 설치하는 방법으로 해서 SQLite3 DB 인식이 가능한 걸 확인했다.

 

yum -y install python3
python3 -V

 

파이썬 장고에서 DB 인식할 때 SQLite 3.9.0 이상으로 설치를 하라고 나온다.

기본 설치된 버전은 3.7.X 버전이다.

구글링을 하면 3.8.11 로 업데이트하는 방법이 나온다.

cd /root
mkdir sqlite3
cd sqlite3

wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.10.2/1.fc22/x86_64/sqlite-3.10.2-1.fc22.x86_64.rpm
wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.10.2/1.fc22/x86_64/sqlite-devel-3.10.2-1.fc22.x86_64.rpm
sudo yum -y install sqlite-3.10.2-1.fc22.x86_64.rpm sqlite-devel-3.10.2-1.fc22.x86_64.rpm
sqlite3 –version

 

https://kojipkgs.fedoraproject.org//packages/sqlite/ 에서 더 높은 버전을 받을 수 있으나, 더 상위버전으로 했을 경우 에러가 발생할 수 있다.

 

파이썬 3.6.8 버전 설치는 끝났다.

이제 가상환경 설정을 하는 방법이다. 설치할 디렉토리는 아래와 같이 /home/httpd/python/ 으로 정했다.

mkdir -p /home/httpd/python/
cd /home/httpd/python/
python3 -m pip install virtualenv


python3 -m pip install --upgrade pip

 

virtualenv django


source /home/httpd/python/django/bin/activate

 

장고 설치

pip3 install Django

 

장고 REST Framework 설치

pip3 install djangorestframework

여기까지 하면 프로젝트 생성 이전까지 준비는 다 되었다.

직접 프로젝트를 생성해도 되고, 윈도우 Vistual Studio Code 환경에서 만든 코드를 Upload하여 수정해도 된다.

 

그럼 여기에서는 윈도우10 환경에서 개발 연습한 코드를 리눅스(CentOS 7.X)에 import 하는 방법을 적는다.

프로젝트 직접 생성시에는

cd /home/httpd/python/
django-admin startproject fc_django
cd fc_django
django-admin startapp fcuser
django-admin startapp product
django-admin startapp order

순서로 명령어를 입력하면 프로젝트 및 앱이 생성된다.

 

파일 샘플은 https://link2me.tistory.com/2014 에서 받으면 된다.

파일 Upload를 한 다음에 unzip 으로 압축을 푼다.

윈도우에는 실행권한이 있는 파일 구분이 없기 때문에 manage.py 파일이 644 권한이다.

권한을 755로 변경하여 실행할 수 있는 권한을 부여한다.

cd /home/httpd/python/fc_django
chmod 755 manage.py

 

python manage.py makemigrations

python manage.py migrate

 

python manage.py createsuperuser

관리자 ID는 원하는 아이디로 지정하면 된다.

 

이제 서버를 구동시켜 보자.

먼저 CentOS7 에서 구동될 방화벽을 먼저 설정해야 한다.

8000 번 포트로 개발서버를 구동시킬 것이기 때문에 포트를 등록해야 한다.

# 방화벽 설정
yum -y install firewalld

# 방화벽 데몬 시작
systemctl start firewalld

# 서버 부팅 시 firewalld 데몬 자동 시작 설정
systemctl enable firewalld

firewall-cmd --permanent --add-service=http 
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-service=mysql
firewall-cmd --permanent --zone=public --add-port=3306/tcp

firewall-cmd --permanent --zone=public --add-port=8000/tcp
firewall-cmd --reload
firewall-cmd --list-all

 

서버 구동
python manage.py runserver 0.0.0.0:8000

 

방화벽이 오픈되어 있는데 아래와 같은 메시지 나온다면....

 

settings.py 파일을 수정해야 한다.

 

 

 

 

 

Control + C 로 빠져나와 다시 개발 서버를 구동시킨다.

 

Web 브라우저 에서 확인하면 아래 그럼처럼 정상적으로 Web 이 구동되는 걸 알 수 있다.

 

이상으로 CentOS 7 에서 파이썬 개발서버를 구동하여 프로젝트를 옮겨서 동작되는 과정을 살펴봤다.

GitHub 등에서 구한 소스를 이런 방법으로 구동시켜 볼 수 있다고 보면 될 것이다.

 

공인(Public) IP에서도 접속 가능하게 IPTIME 공유기 포트포워딩을 설정해줬더니 공인 IP에서도 잘 접속된다.





 

 

 

 

 

블로그 이미지

Link2Me

,
728x90

패스트 캠퍼스 장고 강좌를 듣고 약간 Django 플랫폼에 대해 이해를 한 거 같다.

패스크 캠퍼스 장고 강좌는 무조건 강좌를 따라서 실행해야만 하는 어려움이 있다.

단계별로 설명하기 때문에 도움이 되는 건 분명하다.

 

배우는 초보 입장에서는 동작해서 실행되는 결과를 어떻게 만드는지부터 알고 싶다는 것이다.

대략 구조 분석에 대한 사항은 유투브 동영상을 참조하다보면 이해가 될 것이다.

 

1. 가상환경 및 프로젝트 생성

https://link2me.tistory.com/2011

 

파이썬 Windows 기반 장고 설치

먼저 파이썬이 설치되어 있어야 한다. 그 다음에 pip install virtualenv 를 해서 가상환경 만들 준비를 한다. 사용법 예시 virtualenv PythonDjango cd Scripts activate.bat pip install Django python -m pip..

link2me.tistory.com

를 참조하면 프로젝트 생성은 할 수 있다.

 

2. 앱 생성 및 SQLite DB 생성

- 프로젝트 생성

  django-admin startproject fc_django

- 생성된 프로젝트 폴더로 이동하여 앱을 생성한다.

  cd fc_django

  django-admin startapp fcuser

  django-admin startapp product

  django-admin startapp order

  이 단계까지 생성한 파일을 첨부한다.

fc_django_01.zip
0.01MB

- 앱을 생성하고 나서, 해야 할 사항은 앱 폴더내의 models.py 에 SQLite DB에 연결할 Class를 정의한다.

  생성한 앱 모두 models.py 에 Class 를 정의해야 한다.

- 프로젝트 생성시 만들어진 폴더에 settings.py 폴더에 추가 생성한 앱을 등록해야 한다.

- 그 다음에 manage.py 파일이 있는 폴더에서 SQLite DB 생성하는 명령어를 수행한다.

   python manage.py makemigrations

   python manage.py migrate

   settings.py 에 앱을 추가 등록하지 않으면 위 명령어가 동작되지 않는다.

   첨부 파일은 이 단계까지 진행한 파일을 압축한 것이다.

   몇차례 시행착오를 거치면서 해당 진행단계까지 진행한 파일을 압축해두는 것이 좋을 듯해서다.

fc_django_02.zip
0.03MB

- admin.py 에 코드를 추가하고 나서 python manage.py createsuperuser 를 해서 Username, Email, Password 를 등록한다.

이제 관리자 화면을 실행해서 보자.

http://127.0.0.1:8000/admin/

 

 

회원 가입, 로그인처리까지 작성된 파일이다.

http://127.0.0.1:8000/register/
http://127.0.0.1:8000/login/

fc_django_04.zip
0.04MB

superuser 아이디 생성을 한 부분 즉, 패스워드를 알 수가 없기 때문에 이 파일에서 참조할 사항은 fcuser 폴더의 forms.py, views.py 와 fc_django 폴더의 urls.py 파일이다.

templates 폴더는 그대로 활용하면 된다.

 

개발 서버를 구동한 상태에서 파일을 수정하면 에러가 발생하면 바로 바로 화면에 표시가 되므로 코드 구현시 도움이 많이 된다.

 

REST Framework 기능 이전까지의 코드가 제대로 동작되는 걸 확인할 수 있다.

fc_django_06_fin.zip
0.05MB

 

REST Framework 를 이용하기 위해서는

https://link2me.tistory.com/2015

를 참조하시라.

 

 

블로그 이미지

Link2Me

,
728x90

CentOS 7 리눅스에는 기본적으로 파이썬 2.7이 설치되어 있다.

기존에 설치된 건 버전이 낮아서 사용이 불가능하다.

아래 명령어는 리눅스 관리자권한에서 직접 붙여넣기 편하게 앞에 #을 제거했다. #은 주석으로 인식하므로...

메모장 또는 EditPlus 같은 에디터를 이용하여 아래 내용을 Drag 하고 복사하여 붙여넣기를 한 다음에 CentOS 7 에서 명령어를 입력하면 편리하다.

yum -y install epel-release

yum -y install gcc openssl-devel bzip2-devel libffi-devel sqlite-devel

#yum -y install sqlite*

# root 디렉토리 또는 임의의 디렉토리에서 실행한다.

# python 최신 버전을 확인하려면 https://www.python.org/ftp/python/ 를 한다. 그리고 아래 빨간색 부분을 변경

wget https://www.python.org/ftp/python/3.9.5/Python-3.9.5.tgz
tar -xvf Python-3.9.5.tgz
cd Python-3.9.5/
./configure --enable-optimizations

make altinstall

# make && make intall 절대 하지 말길

 

# 설치된 파이썬 3.9 바이너리 파일이 어디있는지 확인한다.
which python3.9

 

# bash파일에 파이썬 별칭을 만든다.
vi /root/.bashrc

alias python3="/usr/local/bin/python3.9"
를 추가하고 :wq 로 빠져나온다.

 


# source명령어로 적용시킨다.
source /root/.bashrc

 

# 파이썬 버전을 출력해보면 된다.

# python3.9를 python 으로 변경하면 yum 설치 등 환경설정을 별도 작업해줘야 할 일이 좀 있어서 그냥 python3 으로 설치를 했다.

yum -y install python 으로 설치하면 python 3.6.8 이 기본 설치되어 소스 설치로 좀 더 높은 버전을 설치했다.

 

 

# 원래 상태로 되돌리기

   - 위에 입력한 alias 라인을 지운다. (저장후 나감)
   # hash -r  → bash cache를 초기화 (alias삭제후 python3 명령어 치면 계속  /usr/local/bin/python3.9 를 찾는다)

 

 

# yum 동작을 위한 파이썬
vi /usr/bin/yum
#!/usr/bin/python 으로 되어 있다.
만약 yum install python3 를 했다면 같은 디렉토리에 파이썬 2.7 버전과 3.6 버전이 설치된다.
이 경우에는 
#!/usr/bin/python2.7
로 명확하게 지정해줘야 yum 설치 기능이 동작된다.

내 경우는 python3.9 를 별도 소스 설치했고 python3 로 동작시키도록 했으므로 굳이 변경할 필요는 없다.

 

가상환경 설정

 

 

가상환경 만들기

먼저 가상환경을 만들 디렉토리를 설정한다. 가상환경 이름은 django 라고 설정했다.

mkdir -p /home/httpd/python

cd /home/httpd/python

virtualenv django

 

가상환경 활성화

source django/bin/activate

또는 절대경로 전체를 적어줘도 된다.

가상환경 비활성화

deactivate

 

장고 설치

pip install django

 

장고 REST Framework 설치

pip install djangorestframework

여기까지는 문제없이 잘되었다.

문제는 SQLite3 DB 인식을 하도록 하는 것에서 실패하여 파이썬3를 yum 설치하는 방법으로 해결했다.

블로그 이미지

Link2Me

,
728x90

터미널(CMD) 창에서 아래 명령어를 입력한다.

pip install djangorestframework

 

개발 서버를 구동시키는데 동작이 안되고 아래와 같은 에러 메시지가 발생한다.

 

VSCode 상에서 다시 실행을 했더니 동작된다.

python -m pip install djangorestframework

 

개발 서버가 잘 구동되고 있다는 걸 알 수 있다.

블로그 이미지

Link2Me

,
728x90

먼저 파이썬이 설치되어 있어야 한다.

그 다음에 pip install virtualenv 를 해서 가상환경 만들 준비를 한다.

 

사용법 예시

virtualenv PythonDjango

cd Scripts

activate.bat

pip install Django

python -m pip install --upgrade pip

가상환경에서 장고 설치를 했다.

장고 모듈이 제대로 설치되었는지 여부는 py -m django --version

 

프로젝트 만들기

앱은 특정한 기능을 수행하는 웹 어플리케이션을 말한다.

프로젝트는 이런 특정 웹 사이트를 위한 앱들과 각 설정들을 같이 묶어놓은 것이다.

board 폴더에서 templates 폴더를 생성한다.

 

아래그림에서 장고 프레임웍의 기본구조를 확인할 수 있다.

장고 라이프사이클

Web 서버는 실제 운용시 Apache 또는 Nginx 서버

 

개발 서버

Django 프로젝트가 제대로 동작하는지 확인하는 방법

py manage.py runserver

 

이제 본인의 웹브라우저에서 http://127.0.0.1:8000 으로 검색하면 아래와 같이 나온다.

 

이제 간단하게 앱(기능)을 하나 만들어서 테스트를 해보자

https://docs.djangoproject.com/

 

Django documentation | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

를 입력하고 아래 그림의 순서대로 따라하면 된다.

영문을 한글로 바꾸는 것은 en 부분을 ko 로 변경하자.

https://docs.djangoproject.com/ko/3.2/intro/

 

시작하기 | Django 문서 | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

블로그 이미지

Link2Me

,
728x90

파이썬을 이용하여 엑셀을 읽고 특정 셀에 기록하는 코드 간단 예제이다.

 

# pip install requests
# pip install openpyxl
 
import requests
import openpyxl
import json
 
## 엑셀 파일 오픈
filename = "address.xlsx"
wb = openpyxl.load_workbook(filename)
 
## 시트 설정
# sheet = wb.worksheets[0] # active 시트 열기
sheet = wb['Sheet3']  # 시트명 직접 적어주기
 
## 데이터 가져오기
rowCount = 2
for row in sheet.rows:    
 
    try:
        ## 엑셀 읽어오기
        read_cell = row[1].value
        print(read_cell)
 
        ## cell 설정 
        lat_cell = sheet.cell(row = rowCount, column = 3)  # C열은 3
        lng_cell = sheet.cell(row = rowCount, column = 4)
 
        ## 데이터 추가
        lat_cell.value = "위도"
        lng_cell.value = "경도"
 
    except KeyError as ke:
        lat_cell.value = 0
        lng_cell.value = 0
    except TypeError as te:
        lat_cell.value = 0
        lng_cell.value = 0
        
    rowCount = rowCount + 1
 
## 데이터 저장 (엑셀 파일이 열린 상태에서는 저장 실패)
wb.save("address.xlsx")
 
# 닫기
wb.close()
 

 

'파이썬 > Python 활용' 카테고리의 다른 글

[Python] mariadb connection in a class  (0) 2022.03.16
[Python] Naver API 주소 좌표변환 예제  (0) 2022.03.16
블로그 이미지

Link2Me

,
728x90

네이버 뉴스 스탠드 정보를 크롤링하는 예제이다.

 

 

소스코드 구현

# 네이버 뉴스 스탠드 크롤링
# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
 
options = Options()
options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# webdirver 설정(Chrome, Firefox 등)
chromedriver_autoinstaller.install()
driver = webdriver.Chrome(options=options) # 브라우저 창 안보이기
# driver = webdriver.Chrome() # 브라우저 창 보이기
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
baseURL = 'https://www.naver.com/'
driver.get(baseURL)
 
soup = BeautifulSoup(driver.page_source, 'html.parser')
news_stand = soup.select('div.thumb_area > div')
 
print('-' * 25, end=' ')
print('네이버 뉴스 스탠드',end=' ')
print('-' * 25)
for v in news_stand:
    title = v.select_one('a.thumb > img').get('alt')
   news_url_all = v.select('div.popup_wrap > a')
    for x in news_url_all:
        if x.text.strip() == '기사보기':
           news_url = x.get('href')
    print(title, ', ', news_url)
print('-' * 70)
 

 

crawling_24.py
0.00MB

블로그 이미지

Link2Me

,
728x90

클래스와 함수를 사용한 완성본(?)이라고 볼 수 있는 예제이다.

이미지를 엑셀에 저장하는 것은 성공하지 못했다.

 

전체 페이지가 얼마인지 값을 가져오는 로직을 추가로 구현했다.

 

 

# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
# pip install xlsxwriter
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
import xlsxwriter  # 엑셀 처리 임포트
from io import BytesIO # 이미지 바이트 처리
import requests
 
class CRAWL:
    def __init__(self):
        self.options = Options()
        self.options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
        chromedriver_autoinstaller.install()
        self.driver = webdriver.Chrome(options=self.options) # 브라우저 창 안보이기
        # self.driver = webdriver.Chrome() # 브라우저 창 보이기
 
        # Excel 처리 선언
        self.savePath = "./"
        self.workbook = xlsxwriter.Workbook(self.savePath + 'crawling_result.xlsx')
        # 워크 시트
        self.worksheet = self.workbook.add_worksheet()
 
        self.totalPage = 1 # 전체 페이지수
 
    def getBaseURL(self,url):
        self.driver.implicitly_wait(5)  # 크롬 브라우저 내부 대기 (암묵적 대기)
        self.driver.set_window_size(1920,1280# 브라우저 사이즈
        self.driver.get(url)
 
        # 제조사별 검색
        mft_xpath = '//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'
        WebDriverWait(self.driver,3).until(EC.presence_of_element_located((By.XPATH,mft_xpath))).click()
        
        # 원하는 모델 카테고리 클릭
        model_xpath = '//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'
        WebDriverWait(self.driver,3).until(EC.presence_of_element_located((By.XPATH,model_xpath))).click()
 
        time.sleep(3# 검색 결과가 랜더링될 때까지 잠시 대기
 
    def getTotalPage(self,url):
        self.getBaseURL(url)
        soup = BeautifulSoup(self.driver.page_source, 'html.parser')
        page_list = soup.select('div.number_wrap > a.num')
        for v in page_list:
            cur_Page = v.text.strip()
        self.totalPage = cur_Page
        del soup
 
    def crawling(self,url):
 
        self.getTotalPage(url)
 
        totalPage = int(self.totalPage)  # 크롤링할 전체 페이지수
        print('total Pages : ',totalPage)
        curPage = 1  # 현재 페이지
        excel_row = 1 # 엑셀 행 수
        
        self.worksheet.set_column('A:A'40# A 열의 너비를 40으로 설정
        self.worksheet.set_row(0,18# A열의 높이를 18로 설정
        self.worksheet.set_column('B:B'12# B 열의 너비를 12로 설정
        self.worksheet.set_column('C:C'60# C 열의 너비를 60으로 설정
        
        self.worksheet.write(00'제품 모델명')
        self.worksheet.write(01'가격')
        self.worksheet.write(02'이미지 URL')
 
        while curPage <= totalPage:
            #bs4 초기화
            soup = BeautifulSoup(self.driver.page_source, 'html.parser')
            # 상품 리스트 선택
            goods_list = soup.select('li.prod_item.prod_layer')
        
            # 페이지 번호 출력
            print('----- Current Page : {}'.format(curPage), '------')
        
            for v in goods_list:
                # 상품모델명, 가격, 이미지
                name = v.select_one('p.prod_name > a').text.strip()
                if not 'APPLE' in name:  # 개발자 모드 및 브라우저 상에서는 보이지 않는 데이터 대상에서 제외
                    continue
                price = v.select_one('p.price_sect > a').text.strip()
                img_link = v.select_one('div.thumb_image > a > img').get('data-original')
                if img_link == None:
                    img_link = v.select_one('div.thumb_image > a > img').get('src')
        
                imgLink = img_link.split("?")[0].split("//")[1]
                img_url = 'https://{}'.format(imgLink)
                
                # 이미지 요청 후 바이트 반환
                res = requests.get(img_url) # 이미지 가져옴
                img_data = BytesIO(res.content) # 이미지 파일 처리
                image_size = len(img_data.getvalue()) # 이미지 사이즈 
        
                # 엑셀 저장(텍스트)
                self.worksheet.write(excel_row, 0, name)
                self.worksheet.write(excel_row, 1, price)
        
                # 엑셀 저장(이미지)
                if image_size > 0# 이미지가 있으면
                    # 이미지 저장이 안되어서 그냥 텍스트로 이미지 URL 저장 처리
                    # worksheet.insert_image(excel_row, 2, img_url, {'image_data' : img_data})
                    self.worksheet.write(excel_row,2,img_url) # image url 텍스트 저장
                        
                excel_row += 1 # 엑셀 행 증가
        
            # 페이지별 스크린 샷 저장
            self.driver.save_screenshot(self.savePath + 'target_page{}.png'.format(curPage))
            
            curPage += 1  # 페이지 수 증가
            if curPage > totalPage:
                print('Crawling succeed!')
                break
        
            # 페이지 이동 클릭
            cur_css = 'div.number_wrap > a:nth-child({})'.format(curPage)
            WebDriverWait(self.driver,3).until(EC.presence_of_element_located((By.CSS_SELECTOR,cur_css))).click()
            
            del soup  # BeautifulSoup 인스턴스 삭제
            time.sleep(3# 3초간 대기
 
    def closing(self):    
        self.driver.close()  # 브라우저 종료  
        # 엑셀 파일 닫기
        self.workbook.close() # 저장
         
if __name__ == "__main__":
    baseURL = 'http://prod.danawa.com/list/?cate=112758&15main_11_02'
    crawl = CRAWL()
    crawl.crawling(baseURL)
    crawl.closing()
 

 

블로그 이미지

Link2Me

,
728x90

SQLite3 를 사용하여 간단하게 상품을 등록하고, 구매하고 보여주는 코드 예제이다.

isolation_level=None 을 하면 auto commit 이 된다.

 

import sqlite3
 
# DB 생성은 DB Browser for SQLite 를 이용하여 수동 생성(?) 한 줄 알았는데 그냥 자동 생성된다.
## 데이터베이스를 연결하는 코드
conn = sqlite3.connect("baseballmarket.db", isolation_level=None)
 
# 커서 획득
= conn.cursor()
 
## 상품과 주문 테이블을 생성하는 코드
productList = (('모자','35000'),('배트','100000'),('글러브','150000'))
 
c.execute("CREATE TABLE IF NOT EXISTS productList(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, price INTEGER)")
c.execute("CREATE TABLE IF NOT EXISTS orderList(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, cnt INTEGER, price INTEGER, total INTEGER)")
 
## 상품 데이터를 추가하는 코드
# 상품 데이터 중복 저장 체크
for row in c.execute('SELECT count(*) FROM productList'):
    if row[0== 0:
        c.executemany("INSERT INTO productList (name,price) values (?,?)", productList)
 
## 상품 목록을 표시하는 코드
while True:
    print("------------------상품목록------------------")
    for row in c.execute('SELECT id,name, price FROM productList'):
        print('상품번호 :',row[0],', 상품명 :', row[1], ', 가격 :', row[2])
    print("--------------------------------------------")
 
    print('')
    num = input("구매하실 상품의 번호를 입력해주세요: ")
    c.execute("SELECT name, price FROM productList WHERE id = ?",(num,))
    result = c.fetchone()
 
 
    ## 상품 번호와 주문 수량을 입력받는 코드
    print('')
    #print("구매할 수량을 입력해주세요: ")
    count = int(input("구매할 수량을 입력해주세요: "))
    total = count * int(result[1])
 
    ## 주문 데이터를 db에 추가하는 코드
    c.execute("INSERT INTO orderList (name, cnt, price, total) VALUES (?,?,?,?)", (result[0],count,result[1],total))
 
    ## 현재까지 주문 내역을 출력하는 코드
    print('')
    print("현재까지 구매한 내역 보기")
    print("--------------------주문목록--------------------")
    for row in c.execute('SELECT * FROM orderList'):
        print('상품명 :',row[1],', 주문수량 :', row[2], ', 단가 :', row[3], ', 구매가격 :', row[4])
    print("------------------------------------------------")
 
    print('')
    print("상품을 추가 구매하시겠습니까?\n중단하려면 'X'을 눌러주세요.\n계속 하시려면 엔터키를 눌러주세요. ")
    if(input() == "x"): break
 
print('')
print("총 구매가격", end='')
for row in c.execute('SELECT sum(total) FROM orderList'):
    print(' : ',row[0],'원')
print('')
 
conn.close()
 

 

 

'파이썬 > Python 기초' 카테고리의 다른 글

Python SQLite DB 연동 샘플  (0) 2022.01.21
[Python] MariaDB 연결  (0) 2021.05.20
csv 파일 다루기  (0) 2021.05.11
람다 표현식으로 함수 만들기  (0) 2021.05.10
[파이썬기초] list comprehension  (0) 2021.05.04
블로그 이미지

Link2Me

,
728x90

https://link2me.tistory.com/2003 에서 작성한 코드를 수정하여 엑셀로 저장하기 위한 부분을 보완하고 엑셀 저장을 시도했다.

텍스트 저장은 잘 되는데 엑셀에 이미지 저장하는 것은 실패가 된다.

이미지를 저장하는 속도 문제인지 여부는 테스트를 좀 더 해봐야 할 거 같다.

그래서 이미지 URL 을 텍스트로 저장하는 로직으로 코드를 구현했다.

 

# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
# pip install xlsxwriter
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
# 엑셀 처리 임포트
import xlsxwriter
# 이미지 바이트 처리
from io import BytesIO
import requests
 
# 다나와 사이트 검색
 
options = Options()
options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# Excel 처리 선언
savePath = "c:/Users/zx/Documents/Python/"
workbook = xlsxwriter.Workbook(savePath + 'crawling_result.xlsx')
 
# 워크 시트
worksheet = workbook.add_worksheet()
 
# webdirver 설정(Chrome, Firefox 등)
chromedriver_autoinstaller.install()
driver = webdriver.Chrome(options=options) # 브라우저 창 안보이기
# driver = webdriver.Chrome() # 브라우저 창 보이기
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
driver.get('http://prod.danawa.com/list/?cate=112758&15main_11_02')
 
# 페이지 내용
# print('Page Contents : {}'.format(driver.page_source))
 
# 제조사별 검색 (XPATH 경로 찾는 방법은 이미지 참조)
mft_xpath = '//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,mft_xpath))).click()
 
# 원하는 모델 카테고리 클릭 (XPATH 경로 찾는 방법은 이미지 참조)
model_xpath = '//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,model_xpath))).click()
 
# 2차 페이지 내용
# print('After Page Contents : {}'.format(driver.page_source))
 
# 검색 결과가 렌더링 될 때까지 잠시 대기
time.sleep(3)
 
# 현재 페이지
curPage = 1
 
# 크롤링할 전체 페이지수
totalPage = 6
 
# 엑셀 행 수
excel_row = 1
 
worksheet.set_column('A:A'40# A 열의 너비를 40으로 설정
worksheet.set_row(0,18# A열의 높이를 18로 설정
worksheet.set_column('B:B'12# B 열의 너비를 12로 설정
worksheet.set_column('C:C'60# C 열의 너비를 60으로 설정
 
worksheet.write(00'제품 모델명')
worksheet.write(01'가격')
worksheet.write(02'이미지')
 
while curPage <= totalPage:
    #bs4 초기화
    soup = BeautifulSoup(driver.page_source, 'html.parser')
    # 상품 리스트 선택
    goods_list = soup.select('li.prod_item.prod_layer')
 
    # 페이지 번호 출력
    print('----- Current Page : {}'.format(curPage), '------')
 
    for v in goods_list:
        # 상품모델명, 가격, 이미지
        name = v.select_one('p.prod_name > a').text.strip()
        if not 'APPLE' in name:
            continue
        price = v.select_one('p.price_sect > a').text.strip()
        img_link = v.select_one('div.thumb_image > a > img').get('data-original')
        if img_link == None:
            img_link = v.select_one('div.thumb_image > a > img').get('src')
 
        imgLink = img_link.split("?")[0].split("//")[1]
        img_url = 'https://{}'.format(imgLink)
        
        # 이미지 요청 후 바이트 반환
        res = requests.get(img_url) # 이미지 가져옴
        img_data = BytesIO(res.content) # 이미지 파일 처리
        image_size = len(img_data.getvalue()) # 이미지 사이즈 
 
        # 엑셀 저장(텍스트)
        worksheet.write(excel_row, 0, name)
        worksheet.write(excel_row, 1, price)
 
        # 엑셀 저장(이미지)
        if image_size > 0# 이미지가 있으면
            # worksheet.insert_image(excel_row, 2, img_url, {'image_data' : img_data})
            worksheet.write(excel_row,2,img_url) # image url 텍스트 저장
 
        # 엑셀 행 증가
        excel_row += 1
 
        # print(name,', ', price,', ', img_url)
    print()
 
    # 페이지 수 증가
    curPage += 1
 
    if curPage > totalPage:
        print('Crawling succeed!')
        break
 
    # 페이지 이동 클릭
    cur_css = 'div.number_wrap > a:nth-child({})'.format(curPage)
    WebDriverWait(driver,3).until(EC.presence_of_element_located((By.CSS_SELECTOR,cur_css))).click()
 
    # BeautifulSoup 인스턴스 삭제
    del soup
 
    # 3초간 대기
    time.sleep(3)
 
# 브라우저 종료
driver.close()    
 
# 엑셀 파일 닫기
workbook.close() # 저장
 

 

블로그 이미지

Link2Me

,
728x90

이전 예제에서는 1페이지에 있는 내용만 크롤링했다면, 이번 예제에서는 모든 페이지를 전부 크롤링하는 방법이다.

 

# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
 
# 다나와 사이트 검색
 
options = Options()
options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# webdirver 설정(Chrome, Firefox 등)
chromedriver_autoinstaller.install()
driver = webdriver.Chrome(options=options) # 브라우저 창 안보이기
# driver = webdriver.Chrome() # 브라우저 창 보이기
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
driver.get('http://prod.danawa.com/list/?cate=112758&15main_11_02')
 
# 페이지 내용
# print('Page Contents : {}'.format(driver.page_source))
 
# 제조사별 검색 (XPATH 경로 찾는 방법은 이미지 참조)
mft_xpath = '//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,mft_xpath))).click()
 
# 원하는 모델 카테고리 클릭 (XPATH 경로 찾는 방법은 이미지 참조)
model_xpath = '//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,model_xpath))).click()
 
# 2차 페이지 내용
# print('After Page Contents : {}'.format(driver.page_source))
 
# 검색 결과가 렌더링 될 때까지 잠시 대기
time.sleep(2)
 
# 현재 페이지
curPage = 1
 
# 크롤링할 전체 페이지수
totalPage = 6
 
while curPage <= totalPage:
    #bs4 초기화
    soup = BeautifulSoup(driver.page_source, 'html.parser')
 
    # 상품 리스트 선택
    goods_list = soup.select('li.prod_item.prod_layer')
 
    # 페이지 번호 출력
    print('----- Current Page : {}'.format(curPage), '------')
 
    for v in goods_list:
        # 상품명, 가격, 이미지
        name = v.select_one('p.prod_name > a').text.strip()
        if not 'APPLE' in name:
            continue
        price = v.select_one('p.price_sect > a').text.strip()
        img_link = v.select_one('div.thumb_image > a > img').get('data-original')
        if img_link == None:
            img_link = v.select_one('div.thumb_image > a > img').get('src')
        print(name,', ', price,', ', img_link)
    print()
 
    # 페이지 수 증가
    curPage += 1
 
    if curPage > totalPage:
        print('Crawling succeed!')
        break
 
    # 페이지 이동 클릭
    cur_css = 'div.number_wrap > a:nth-child({})'.format(curPage)
    WebDriverWait(driver,3).until(EC.presence_of_element_located((By.CSS_SELECTOR,cur_css))).click()
 
    # BeautifulSoup 인스턴스 삭제
    del soup
 
    # 3초간 대기
    time.sleep(3)
 
# 브라우저 종료
driver.close()    
 
블로그 이미지

Link2Me

,