728x90

이글은 완전 기초적인 CRUD 구현방법에 대해 설명해드린느 글입니다.

 


CRUD란?

📥 Create : 데이터 생성
👀 Read : 데이터 불러오기
✍️ Update : 데이터 수정 / 갱신
📤 Delete : 데이터 삭제하기

 

 

 

<1.시작>

#1.장고설치 
pip install django
#2. 장고 프로젝트만들기
django-admin startproject config .
#3. 데이터베이스DB 생성
python manage.py migrate
#4. 앱생성
python manage.py startapp myapp

 

 

<2.프로젝트 구성>

>

 

3. 소스코드

#myproject/urls.py

from django.contrib import admin
from django.urls import path , include

#사용자가 사이트에 접속했을떄 요청을 어떻게 누가 처리할건지 지정하는 
#라우팅을 해주는 파일 

# https://127.0.0.1/
# https://127.0.0.1/app/
# https://127.0.0.1/create/
# https://127.0.0.1/read/1/
urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('myapp.urls'))
]
#myapp/urls.py

#장고의 urls.py 파일은 페이지 요청이 발생하면 가장 먼저 호출되는 파일로 
#URL과 뷰 함수 간의 매핑을 정의한다. 뷰 함수는 views.py 파일에 정의된 함수를 말한다.
# 본 프로젝트에서는 글생성, 읽기, 삭제, 수정 이 있기때문에 4개의 path를 추가해주었다.

from django.contrib import admin
from django.urls import path , include
from myapp import views

urlpatterns = [
    path('',views.index),
    path('create/',views.create),
    path('read/<id>/',views.read),
    path('delete/' , views.delete),
    path('update/<id>/',views.update)
    
]

 

#myapp/views.py

from django.shortcuts import render, HttpResponse
import random
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import redirect
nextId = 4
topics = [
{'id':1, 'title':'routing', 'body':'Rounting is ..'},
{'id':2, 'title':'view', 'body':'view is ..'},
{'id':3, 'title':'model', 'body':'model is ..'}
]

def HTMLTemplate(articleTag,id=None):
    
    global topics
    ol = ''
    contextUI =''
    if id != None:
        contextUI = f'''
            <li>
                <form action="/delete/" method="post">
                    <input type="hidden" name="id" value={id}>
                    <input type="submit" value="delete">
                </form>
            </li>

            <li>
                <a href="/update/{id}">update</a>
            </li>

    '''

    for topic in topics:
        ol +=  f'<li><a href = "/read/{topic["id"]}">{topic["title"]}</a></li>'
    
    return (f'''
    <html>
        <body>
        <h1><a href="/" > Django</a></h1>
        <ol>
            {ol}
        </ol>
        {articleTag}

        <ul>
            <li><a href="/create/">create</a></li>
            {contextUI}
            
        </ul>
        
        </body>
        </html>
                                      
    ''')

def index(request):
    article = '''
    <h2> Welcome</h2>
    Hello Django
    '''

    return HttpResponse(HTMLTemplate(article))

def read(request,id):
    global topics
    article =''

    for topic in topics:
        if topic['id'] == int(id):
            article = f'<h2>{topic["title"]}</h2>{topic["body"]}'

    return HttpResponse(HTMLTemplate(article,id))

@csrf_exempt
def create(request):
    global nextId

    print('request.method : ' , request.method)
    if request.method == 'GET':       
        article ='''
            <form action= "/create/" method="post">
                <p> <input type="text" placeholder="title" name="title"> </p>
                <p> <textarea name="body" placeholder="body"> </textarea> </p>
                <p> <input type="submit"></p>
            </form>
        '''
        return HttpResponse(HTMLTemplate(article))
    elif request.method =='POST':
        title = request.POST['title']
        body = request.POST['body']

        newtopic = {"id":nextId , "title":title, "body":body}

        url = '/read/' + str(nextId)
        nextId = nextId + 1
        topics.append(newtopic)
        return redirect(url)
    
@csrf_exempt
def delete(request):
    global topics

    if request.method == 'POST':
        id = request.POST['id']
        newTopics = []
        for topic in topics:
            if topic['id'] != int(id):
                newTopics.append(topic)
        topics = newTopics
        return redirect('/')
    
@csrf_exempt
def update(request,id):
    global topics
    if request.method == 'GET':

        for topic in topics:
            if topic['id'] == int(id):
                selectedTopic ={
                    "title":topic['title'],
                    "body":topic['body']
                }
        article = f'''
            <form action ="/update/{id}/" method="post">
                <p> <input type="text" name="title" placeholder="title"></p>
                <p> <textarea name="body" placeholder="body">{selectedTopic['body']}</textarea></p>
                <p> <input type="submit"></p>
            </form>
        '''
        return HttpResponse(HTMLTemplate(article,id))
    elif request.method =='POST':
        title = request.POST['title']
        body = request.POST['body']
        
        for topic in topics:
            if topic['id'] == int(id):
                topic['title'] = title
                topic['body'] = body
        return redirect(f'/read/{id}')

 

<4. 결과화면 >

 

 

< 5.느낀점 >

Django를 처음 공부하면서 배우는건데 파이썬과 웹작동방식을 조금 알고있으니 빨리 빨리 이해되는거같다.

html css db이용해서 웹페이지 게시판을 처음 구현할때 1주일이 넘는시간이 걸렸던거같은데.... 

 

쨋든 개발을 다하고나서 글쓰기에 <script>alert('test')</script> 스크립트문을 넣었더니 그대로 실행되더라... 보안적인 부분은 아직 장고라는 프레임워크를 배우는단계라 고민만 해보고 다음 단계로 넘어가야겠다. 

728x90

'Python > Django' 카테고리의 다른 글

파이썬 Django 웹 개발 환경준비하기  (0) 2023.09.06
728x90

1. 웹 개발 환경준비

1.장고설치 
pip install django
2. 장고 프로젝트만들기
django-admin startproject config .
3. 데이터베이스DB 생성
python manage.py migrate

2. 프로젝트 구조 

1. config 폴더 : 프로젝트 설정 파일과 웹 서비스 실행을 위한 파일이 들어 있습니다.

   __init__.py : 파이썬 2.x 버전과 호환을 위해 만들어진 비어있는 파일 

   settings.py : 프로젝트 설정에 관한 내용이 담긴 파일

      BASE_DIR : 프로젝트 루트폴더, 설정 파일이나 py파일 등에서 프로젝트의 루트 폴더를 찾아 그 하위를 탐색함, 이 때         문에 변수를 미리 준비해 두는 값

      SECRET_KEY: 보안을 위해 사용되는값, 세션값의 보호나 비밀번호 변경시 사용되는 보안URL을 만드는데 이용

      DEBUG: 디버그 모드를 설정합니다. True 일경우 다양한 오류 메시지ㅣ를  즉시 확인가능 *배포시에는 False 

      allowed_hosts : 현재 서비스의 호스트를 설정 *개발시에는 비어두고 배포시 '*'나 실제 도메인을 사용함

      INSTALLED_APPS: 장고 웹서비스는 다양한 앱의결합으로 만들어지는데, 현재 프로젝트에서 사용하는 앱의 목록을        기록하고 관리합니다.

      MIDDLEWARE: 장고의 모든 요청응답메시지 사이에 실행되는 특수한 프레임워크

      ROOT_URLCONF:기준이 되는 urls.py파일의 경로를 설정

      TEMPLATES: 장고에서 사용하는 템플릿 시스템에 대한 설정들

      WSGI_APPLICATION: 실행을 위한 WSGI 어플리케이션을 설정함

      DATABASE: DB관련 설정

      AUTH_PASSWORD_VALIDATORS : 비밀번호 검을위한 설정

      LANGUAGE_CODE : 다국어에 관한 설정

 

   urls.py : 특정 기능을 수행하기 위해 접속하는 주소를 url이라고 하고 이를 설정해 주는데, 그 설정을 이 파일에 기록함.

   wsgi.py : 웹 서비스를 실행하기 위한 wsgi관련 내용이 들어있습니다. / 실제로 웹 서버와 장고 애플리케이션 사이 통신 역할을 담당 

 

2. venv 폴더 : 프로젝트 구동에 필요한 가상환경이 들어있는 폴더

3. db.sqlite3 : SQLite3 DB파일

4. manage.py : 장고의 다양한 명령어를 실행하기 위한 파일

 

장고 기본명령어

1. 서버 시작하기

$ python manage.py runserver 8080 기본 웹 서버는 8000 포트를 기본으로 동작한다. 변경하고 싶다면 포트 번호를 적어준다.
$ python manage.py runserver 0.0.0.0:8000 IP를 직접 지정하거나 같은 네트워크 망 안에서 접속이 가능하도록 할 때 왼쪽과 같이 적어준다.
$ python manage.py runserver 0:8000 0은 0.0.0.0의 약어이다. 해당 형태로 동작을 시킬 때는 settings.py에 있는 ALLOWED_HOSTS를 설정해 준다.

서버를 실행 후 종료를 하기 위해서는 [Ctrl+C]를 누르면 된다.

 

 

 

2. 기본 명령어 정리

$ django-admin startproject 프로젝트이름

장고 프로젝트를 만드는 명령이다. 장고를 설치 후에 위와 같이 명령어를 치면 프로 젝트가 생성된다. 아래와 같이 뒤에 .을 붙여주면 새로운 파일에서 프로젝트가 생성되는 것이 아니라 현재 파일에서 프로젝트가 생성된다.

$ django-admin startproject 프로젝트이름 .

 

 

$ python manage.py startapp 앱이름

프로젝트에 기능 단위인 앱을 새로 만들 때 사용한다.

 

 

$ python manage.py makemigrations 어플리케이션의 변경 사항을 추적에 DB에 적용할 내용을 정리한다. 보통 모델의 변경 사항이 있을 때 주로 사용한다.
$ python manage.py migrate 실제 변경사항을 DB에 반영한다.
$ python manage.py showmigrations DB 변경사항 목록과 상태를 출력한다.
$ python manage.py createsuperuser 관리자 계정을 생성한다.
$ python manage.py changepassword 계정의 비밀번호를 변경할 수 있다.

 

$ python manage.py sqlmigrate 실행할 SQL 명령문을 출력한다. 어떤 명령문을 실행할지 확인할 떄 사용하고, 튜닝이 안된 쿼리나 슬로우 쿼리 여부를 확인할 수 있다.
$ python manage.py dumpdata 현재 DB의 내용을 백업할 때 사용한다.
$ python manage.py loaddata 백업 파일에서 DB로 내용을 복구 할 때 사용한다.
$ python manage.py flush DB 테이블은 그대로 두고 테이블의 내용만 전부 삭제한다.
$ python manage.py shell 장소 쉘(shell)을 실행한다. 작성한 모델 등을 불러와 실제로 테스트를 해볼 수 있다.
$ python manage.py dbshell DB에 직접 접근할 수 있는 쉘을 실행한다. 장고 어플리케이션에 문제가 있어 관리자 페이지에 접근할 수 없을 때 보통 shell을 이용해 DB를 수정하는데 SQL 구문을 이용해 직접 수정하고 싶다면 dbshell을 이용할 수 있다.

 

참고 : https://han-py.tistory.com/308 

참고 : 배프의 오지랖 파이썬 웹프로그래밍

 

728x90

'Python > Django' 카테고리의 다른 글

[Django] Django CRUD 간단한 게시판 구현하기  (0) 2023.09.17
728x90

1. 개발배경


아이돌 팬카페에서 일정시간에 등업을 신청할경우 선착순으로 어떤 혜택을 받는거같다(정확하진않음)

그래서 어떤분이 매크로 제작외주를 맡기셨고 수강신청매크로와 비슷한 이론인거같아 제작하게 되었습니다.

 

2. Module


 
 
 
from PyQt5.QtGui import QIcon
import requests
import threading
from PyQt5.QtWidgets import *
from PyQt5 import uic
 
 
 
3. 이론

본 포스팅은 selenium을 이용하여 웹드라이버를 통해 웹을 실행하여 글을 쓰는게 아닌

다음서버로 패킷을 날려 내가 원하는시간에 누구보다 빠르게 글을 쓰는것이 목표입니다.

우선 다음카페에서 글을 쓸때 어떤 URL로 데이터가 송수신 되는지

헤더값은 어떤지

payload(json)값은 어떤지

쿠키값은 어떤식으로 통신하는지 규칙성을 이해해야 합니다. 

이러한 매크로같은경우 패킷분석이 99%이며 코드는 requests.post() 한줄이면 끝납니다.

 

본 포스팅은 오마이걸 팬카페에서 이용자가 원하는시간에 등업신청 패킷을 날리는 매크로 입니다.

등업신청을 할때 요청 URL은

 

쿠키 = {
    'HM_CU' : '5EOWjxEKReG'	,
    'HTS' : 'a8F1CyTfhKDrZ1HEcUNmdQ00'	, #변동값
    'LSID' : '4ozpxSyH8WSPySdNOon1RVWOoHIyJcz_V7bIQxR14PC5g05T4lmugY--B3xgBESvMi346UeveMHApLb6OGyY-4u5BKTvVHrLTQpRqvO5uys2XzS-Q',	#변동값
    'PROF' : '0603012032024076024152UiQPJk7X-6w0mlxoempuua_CKZSggqOGDvo1-CjkaIWcKuZP3z7wEl2W4..NYhR74w00LYYSA9A1_cGNLCyhCzrwOjTi9JCLCPIzyUJ3iNS289Pf8dq.GqUK-I8P-bSfpU2ZfabaifH47uU0fIauQlo8OTLLnHHY.bHDTw00Voisr2G29DEIx7wRa8yZRJAMSaOmydBtoy7RolpDnJ7Cye39yr6LtBtp9ZQ-fDSc34JWkRbso7m6DCIqZGU1g8rlf9KqRak-n_-41UM27eYp-TevH7dyy2XAwBEX56l7PFWutf.kMbC_SE.gc8GDVQ00'	,#이건가 
    'TS' : '1669752762',
}

#HTS , LSID는 변동값 1~2시간마다 값이 갱신되는것같다. 
#쿠키값은 많지만 필수로 요구하는 쿠키는 이정도인거 같다. 
#이렇게 5개의 쿠키값은 다음서버와 통신할때 필수로 요구하는 쿠키값인거같다

 

request_payload=[
                {'questionId': '16436', 'answer': input1},
                {'questionId' : '16437', 'answer': input2},
                {'questionId': '16438', 'answer': input3},
                {'questionId': '16439', 'answer': input4},
                {'questionId': '16440', 'answer': input5},
                {'questionId': '16441', 'answer': input6},
                {'questionId': '16442', 'answer': input7}
                ]
                
#request_payload는 다음카페 등업신청을 할때 대답해야 하는 값들이다. input1~7은 GUI프로그램을 통해
# 값을 입력받았습니다.

 

 

전체코드


from re import L
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
import ccxt
import firebase_admin
from PyQt5.QtGui import QIcon
import requests
import threading

from datetime import datetime

TT = 0
쿠키 = {
    'HM_CU' : '5EOWjxEKReG'	,
    'HTS' : 'a8F1CyTfhKDrZ1HEcUNmdQ00'	, #변동값
    'LSID' : '4ozpxSyH8WSPySdNOon1RVWOoHIyJcz_V7bIQxR14PC5g05T4lmugY--B3xgBESvMi346UeveMHApLb6OGyY-4u5BKTvVHrLTQpRqvO5uys2XzS-Q',	#변동값
    'PROF' : '0603012032024076024152UiQPJk7X-6w0mlxoempuua_CKZSggqOGDvo1-CjkaIWcKuZP3z7wEl2W4..NYhR74w00LYYSA9A1_cGNLCyhCzrwOjTi9JCLCPIzyUJ3iNS289Pf8dq.GqUK-I8P-bSfpU2ZfabaifH47uU0fIauQlo8OTLLnHHY.bHDTw00Voisr2G29DEIx7wRa8yZRJAMSaOmydBtoy7RolpDnJ7Cye39yr6LtBtp9ZQ-fDSc34JWkRbso7m6DCIqZGU1g8rlf9KqRak-n_-41UM27eYp-TevH7dyy2XAwBEX56l7PFWutf.kMbC_SE.gc8GDVQ00'	,#이건가 
    'TS' : '1669752762',
}

url = ''



form_class = uic.loadUiType("untitled.ui")[0]
    


class WindowClass(QMainWindow, form_class) :
    
    global TT

    def __init__(self) :
        super().__init__()
        global 쿠키   
        global TT     
        self.setupUi(self)


        def test_button():
        
            input1 = str(self.textEdit.toPlainText())
            input2 = str(self.textEdit_2.toPlainText())
            input3 = str(self.textEdit_3.toPlainText())
            input4 = str(self.textEdit_4.toPlainText())
            input5 = str(self.textEdit_5.toPlainText())
            input6 = str(self.textEdit_6.toPlainText())
            input7 = str(self.textEdit_7.toPlainText())
            input8 = str(self.textEdit_8.toPlainText())
            
            사이트 = str(self.comboBox.currentText())
            시간 = str(self.textEdit_11.toPlainText())
            if(사이트 == '오마이걸'):
                request_payload=[
                {'questionId': '16436', 'answer': input1},
                {'questionId' : '16437', 'answer': input2},
                {'questionId': '16438', 'answer': input3},
                {'questionId': '16439', 'answer': input4},
                {'questionId': '16440', 'answer': input5},
                {'questionId': '16441', 'answer': input6},
                {'questionId': '16442', 'answer': input7}
                ]

                web_url = 'https://cafe.daum.net/_c21_/api/apply/article/1VTSi/sgo6' #오마이걸 
                dt = datetime.now()

                print('정상작동중')
                #insert(index, element) 
                
                
                time_str = str(dt.hour)+str(dt.minute)+str(dt.second)+str(dt.microsecond)
                
                
                if(len(str(dt.hour)) == 1):
                    time_str = '0'+str(dt.hour)+str(dt.minute)+str(dt.second)+str(dt.microsecond)
                    if(len(str(dt.minute)) == 1):
                        time_str ='0'+str(dt.hour)+'0'+str(dt.minute)+str(dt.second)+str(dt.microsecond)
                        
                    if(len(str(dt.second)) == 1):
                        time_str = '0'+str(dt.hour)+str(dt.minute)+'0'+str(dt.second)+str(dt.microsecond)
                        if(len(str(dt.minute)) == 1):
                            time_str = '0'+str(dt.hour)+'0'+str(dt.minute)+'0'+str(dt.second)+str(dt.microsecond)
                time_str = time_str[0:9]
                시간 = 시간[0:9]

                print('현재 시간  :  '  +time_str)

                print('신청할 시간 : ' + 시간)
                탈출 = 0 
                while(True):
                    dt = datetime.now()

                    time_str = str(dt.hour)+str(dt.minute)+str(dt.second)+str(dt.microsecond)
                
                
                    if(len(str(dt.hour)) == 1):
                        time_str = '0'+str(dt.hour)+str(dt.minute)+str(dt.second)+str(dt.microsecond)
                        if(len(str(dt.minute)) == 1):
                            time_str ='0'+str(dt.hour)+'0'+str(dt.minute)+str(dt.second)+str(dt.microsecond)
                            
                        if(len(str(dt.second)) == 1):
                            time_str = '0'+str(dt.hour)+str(dt.minute)+'0'+str(dt.second)+str(dt.microsecond)
                            if(len(str(dt.minute)) == 1):
                                time_str = '0'+str(dt.hour)+'0'+str(dt.minute)+'0'+str(dt.second)+str(dt.microsecond)
                    time_str = time_str[0:9]
                    
                    if(탈출 == 1):
                        break
                    if(탈출 == 0):
                        print('현재 시간  :  '  +time_str)

                        print('신청할 시간 : ' + 시간)
                        if(int(time_str) > int(시간)):
                            page = requests.post(url=web_url,  json=request_payload,  cookies=쿠키)
                            print(page)
                            print(page.text)
                            탈출 = 1 
                            break
                    
                
                

        self.pushButton_3.clicked.connect(test_button) #로그인 버튼 이벤트



            
if __name__ == "__main__" :
    #QApplication : 프로그램을 실행시켜주는 클래스
    app = QApplication(sys.argv)
    
    #WindowClass의 인스턴스 생성
    myWindow = WindowClass() 

    #프로그램 화면을 보여주는 코드
    myWindow.show()
    #프로그램을 이벤트루프로 진입시키는(프로그램을 작동시키는) 코드
    app.exec_()

 

 

 

4. 작동영상


 

 

bandicam 2022-11-30 15-24-16-828.mp4
9.04MB

728x90
728x90

https://www.youtube.com/watch?v=9l3uNWZOmTw 

 

대학교를 다니다 보면 항상 수강 신청하는 날이 무섭고 되게 떨립니다.

한 과목 한 과목 놓치면 자신의 공강이 사라지거나 , 점심시간이 사라지기도 하며

외롭게 혼자 학교를 다니게 될 수도 있으니까요

 

여기서 컴퓨터에 대해 조금 배우신분들이라면 분명 웹을 통해 수강신청서버와 통신을 하기때문에

코딩을 통해 똑같이 수강신청 서버가 열렸을때 서버로 수강신청 요청을 보내게되면

남들이 수강신청 서버에 접속이 몰릴때 , 따로 웹을통해 접속하지않고도

서버가 열리자마자 수강신청을 넣을수있습니다.

 

 

 

준비


수강신청 Requests 과정은 다음과같습니다.

 

수강신청을 하기전 대학교에 장바구니를 담는 날이 있습니다.

이때 Requests URL과 Cookies값들을 알아내고

과목별 Payload 딕셔너리 값들을 모두 알아내야 합니다.

 

Requests URL과 Payload값은 거의 변할일이 없지만

저희 학교같은경우 Cookies값의  SGJESSIONID값이 로그인할때마다 바뀌었습니다.

 

 

 

 

 

실습

크롬 F12(개발자모드)를 킨 상태에서 Network를 누른다음 

수강신청페이지(장바구니) 에 들어가서 아무과목이나 신청(or 장바구니담기) 버튼을 누릅니다.

그러면 Network 탭을 통해 모든 HTTP통신 과정을 볼 수 있습니다.

여기서 수강신청이 되는 요청과정이 분명있습니다.

저희학교같은경우 aply.do 였으며 

아주대의 경우 수강신청버튼을 눌러도 바로 수강신청서버로 요청이 가지않고

수강신청버튼클릭(요청) -> 보안문자 입력폼(응답) -> 옳게입력했을경우 수강신청서버로 수강신청Requests

 

네트워크 통신과정을 보면

capchaAnswer.action 이 보안문자 통신부분 ( payload값이 보안문자 입력값으로 전달됨) 옳바르게 입력했을경우

Response로 200값이 응답이 오게되고 바로 saveOpenLectureReg.ajax 를 통해 아주대 수강신청서버로 수강신청요청을 날리는것 같다.

 

 

 

 

 


 

 

 

 

 

 

 

 

한과목 수강신청 요청을 날리는 코드를 파이썬으로 짜봤다

우리학교의경우 보안문자 입력같은것이 없기때문에 되게 쉽게 구현이 가능함

다른학교들의경우 보안문자만 잘 해결하면 비슷비슷할거같다

 

실습코드(사회봉사 1학점짜리 수강신청코드)


import requests
import time

url='https://sugang.smu.ac.kr/UcrTlsn/aply.do'

cookies = {
    '_ga':'GA1.3.1726804677.1643137138',
    'WMONID':'jNFh1awnyxs',
    'SGJSESSIONID':'o153YzYsXcjHkQGgCaxr45rfQLQin1k44Zy2nuaxuPqvoQEW7x7qttHuS2NgtTAH.amV1c19kb21haW4vc3VnYW5nMQ=='
}

data = {
    '_AUTH_MENU_KEY': '',
'@d1#strSchYear': '2022',
'@d1#strSmtRcd': 'CMN002.0010',
'@d1#strSesRcd': 'CMN111.0000',
'@d1#strEstDeptCd': '00000',
'@d1#strEstShyr': '9',
'@d1#strSbjNo': 'HBRA1005',
'@d1#strDivcls': '1',
'@d1#strReTlsnSchYear': '',
'@d1#strReTlsnSmtRcd': '',
'@d1#strReTlsnSesRcd': '',
'@d1#strReTlsnSbjNo': '',
'@d#': '@d1#',
'@d1#': 'dmParamAply',
'@d1#tp': 'dm'
}

res = requests.post(url=url,cookies=cookies,data=data)
print("상태코드 :  " +  str(res))
print("수강신청요청결과  : " +  res.text)

 

위 코드를 수강신청 서버가 열릴시간때쯤 반복문을통해 요청을 보내면 빠르게 수강신청이 가능하다.

 

 

https://www.youtube.com/watch?v=9l3uNWZOmTw 

 

 

728x90
728x90

필요 모듈 

1
2
3
import FinanceDataReader as fdr
import pymysql
import csv
cs

 

FinanceDataReader은 FinanceData.KR 에서 만든 오픈소스 금융 데이터 수집 라이브러리 이다.

 

 

  • 한국거래소(KRX)에 상장된 주식종목 리스트와 코넥스(비상장)에 있는 주식종목 리스트: 'KRX', 'KOSPI', 'KODAQ', 'KONEX'
  • 글로벌 주식종목 리스트: 'NASDAQ', 'NYSE', 'AMEX' and 'S&P500', 'SSE'(상해), 'SZSE'(심천), 'HKEX'(홍콩), 'TSE'(도쿄)
  • 개별 종목은 물론 금,원유 같은 원자재 지수도 지원한다. 

설치

pip install finance-datareader
pip install pymysql
pip install csv


참고 PyMySQL는 낮은 수준의 API를 지원하지 않습니다 

요구사항 

  • 파이썬 – 다음 중 하나:
  • MySQL 서버 – 다음 중 하나:

 

소스코드



1

2
3

 
conn = pymysql.connect(host="uws7-176.cafe24.com",user="아이디"
,password="패스워드",db="soll0803",charset="utf8")
curs=conn.cursor()
conn.commit()
cs

 

● pymysql.connect() 메소드를 이용하여 SQL에 연결 

host : 접속할 서버 주소

port : 접속할 서버 포트번호

user : sql id

password : sql password

db : 접속할 데이터베이스

charset : 인코딩

 

● line 1에서 접속이 성공하였다면 conn 객체로부터 cursor()메서드를 호출하여 cursor 객체를 가져옴

 

4
5
 
df = fdr.DataReader(symbol="005930",start='2000'#종목코드 005930 -> 삼성전자
df.to_csv('test.csv') #df를 csv파일로 저장
 
 
cs

#type(df) -> dataframe

 

6
7
= open('test.csv','r') #파일읽기모드
csvReader = csv.reader(f) #csvReader객체 생성
cs

 

 

1
2
3
4
5
6
7
8
9
10
11
for row in csvReader:
    Date1 = (row[0])
    Open1 = row[1]
    High1 = row[2]
    Low1 = row[3]
    Close1 = row[4]
    Volume1 = row[5]
    Change1 = row[6]
 
    sql = "INSERT INTO stock_data (Date,Open,High,Low,Close,Volume,Change2) values(%s,%s,%s,%s,%s,%s,%s)"
    curs.execute(sql,(Date1,Open1,High1,Low1,Close1,Volume1,Change1))

conn.commit()
f.close()
conn.close()

 
cs

curs.execute를통해 sql문 실행 

 

전체소스 코드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import FinanceDataReader as fdr
import pymysql
import csv
 
#삼성전자 주가데이터2020~오늘 데이터를 csv로 변환
#df = fdr.DataReader(symbol="005930",start='2020')
#df.to_csv('test.csv')
 
conn = pymysql.connect(host="uws7-176.cafe24.com",
user="아이디",password="패스워드",db="soll0803",charset="utf8")
curs=conn.cursor()
conn.commit()
 
df = fdr.DataReader(symbol="005930",start='2000'#종목코드 005930 -> 삼성전자
df.to_csv('test.csv')
 
= open('test.csv','r')
csvReader = csv.reader(f)
 
for row in csvReader:
    Date1 = (row[0])
    Open1 = row[1]
    High1 = row[2]
    Low1 = row[3]
    Close1 = row[4]
    Volume1 = row[5]
    Change1 = row[6]
 
    sql = "INSERT INTO stock_data (Date,Open,High,Low,Close,Volume,Change2) values(%s,%s,%s,%s,%s,%s,%s)"
    curs.execute(sql,(Date1,Open1,High1,Low1,Close1,Volume1,Change1))
conn.commit()
f.close()
conn.close()
cs

 

결과

728x90
728x90

개요

 

파이썬으로 request.get을 통해 웹을 크롤링하던 도중 원하는 데이터가 크롤링되지않아 처음엔 당황스러웠다.

하지만 크롤링이 되지않는 이유는 get방식으로 웹이 동작하는게 아니라 post방식을 통해 작동하기 때문이었다.

이를 확인하는 방법은 크롤링할 웹사이트로 이동할때 주소창이 변할경우 get방식으로 작동하고 

주소창이 바뀌지않을경우 post방식으로 작동한다는것을 알수있다.

 

 

특징

GET 방식은 다음과 같은 특징이 있습니다.

- 클라이언트가 입력한 값이 URL과 결합되어 스트링 형태로 서버에 전달됩니다
- 서버의 DB에 정보를 요청하는 것이 아니라 해당 웹페이지에서 보여지는 그대로를 가져오는 것입니다.
- 한번 요청시 데이터 양에 제한이 있습니다.

 

POST 방식은 다음과 같은 특징이 있습니다.

- 클라이언트와 서버 간에 스트링 형태로 그대로 서버에 전달되지 않고 인코딩이라는 과정을 거칩니다.
- 헤더를 통해 요청이 전송되는 방식입니다. 이 때문에 GET 방식과 같이 URL이 노출되지 않습니다.
- 요쳥시 GET 보다 더 많은 양의 정보를 교환할 수 있습니다.
- 페이지에서 날짜를 설정하고 조회하기 버튼을 눌렀을 때 서버는 DB에서 해당 기간에 해당되는 정보를 불러와 우리에게 보여줍니다.  이 때 사용하는 방식이 바로 POST 방식입니다. 조회하기 버튼을 눌렀을 때 URL 이 client 에게 노출이 되지 않으며, GET방식에 비해 더 많은 양의 정보를 받아올 수 있다는 장점이 있습니다. 

 

실습

 

 

>>> import requests

 

크롤링할 데이터는 바이낸스에서 상위트레이더의 포지션을 실시간으로 크롤링하는 프로그램을 짜보겠습니다.

https://www.binance.com/en/futures-activity/leaderboard?type=myProfile&tradeType=PERPETUAL&encryptedUid=CCF3E0CB0AAD54D9D6B4CEC5E3E741D2 

 

Binance Futures Leaderboard | ROI & PNL Rankings | Binance Futures

 

www.binance.com

 

위에 주소로 입력하게 될경우 TraderT라는 닉네임을 가진 트레이더의 포지션을 볼수있는데

이 데이터는 get방식으로 작동하는게 아닌 post방식을 통해 클라이언트가 서버(https://www.binance.com/bapi/futures/v1/public/future/leaderboard/getOtherPosition) 

이부분으로 request.post 요청을 하면 서버에서 응답을 해주는 방식으로 작동한다.

 

 

크롬f12를 눌러 개발자모드를 통해  network를 보면 post동식으로 동작하며

 

response로 원하는 데이터가 response오는걸 확인할수있다.

header쪽을 좀더보면 클라이언트가 서버로 post방식으로 request할때

request payload와 form data 두개의 방식으로 서버에게 전달하는데

 

 

Content-Type: application/json다음과 같은 요청이 있을 수 있습니다.

POST /some-path HTTP/1.1

Content-Type: application/json { "foo" : "bar", "name" : "John" }

AJAX별로 이것을 제출하면 브라우저는 단순히 페이로드 본문으로 제출하는 내용을 보여줍니다. 데이터가 어디에서 오는지 전혀 모르기 때문에 할 수 있는 모든 것입니다.

 

method="POST"and Content-Type: application/x-www-form-urlencoded/ 와 함께 HTML 양식을 제출하면 Content-Type: multipart/form-data요청은 다음과 같을 수 있습니다.

POST /some-path HTTP/1.1 Content-Type: application/x-www-form-urlencoded foo=bar&name=John

이 경우 form-data는 요청 페이로드입니다. 

여기에서 브라우저는 더 많이 알고 있습니다. bar가 제출된 양식의 입력 필드 foo 값이라는 것을 알고 있습니다. 그리고 그것이 당신에게 보여주고 있는 것입니다.

따라서 Content-Type데이터가 제출되는 방식은 다르지만 차이 가 있습니다. 두 경우 모두 데이터는 메시지 본문에 있습니다.

 

출처:https://stackoverflow.com/questions/23118249/whats-the-difference-between-request-payload-vs-form-data-as-seen-in-chrome

 

import requests
import websocket
import time
import json


url = 'https://www.binance.com/bapi/futures/v1/public/future/leaderboard/getOtherPosition'

payload = {
    'encryptedUid':'CCF3E0CB0AAD54D9D6B4CEC5E3E741D2',
    'tradeType':'PERPETUAL'
}
response1 = requests.post(url,json=payload)
#print(type(response1.text))
dict = json.loads(response1.text)
print(list.__len__(dict['data']['otherPositionRetList']))
print('심볼\t: 시작가격\t : 포지션수량\t : 포지션수량(usdt)')

for i in range(0,list.__len__(dict['data']['otherPositionRetList'])):
    print(dict['data']['otherPositionRetList'][i]['symbol'] +' : ' + str(dict['data']['otherPositionRetList'][i]['entryPrice'])+ ' : ' + str(dict['data']['otherPositionRetList'][i]['amount']),end='\t : ')
    usdt_amount = (int(dict['data']['otherPositionRetList'][i]['entryPrice'])*int(dict['data']['otherPositionRetList'][i]['amount']))
    print(usdt_amount)

 

출력결과

728x90

+ Recent posts