Web 크롤링/Python Crawling

네이버 증권 정보 크롤링 예제 2 - 일별 시세 정보 가져오기

Link2Me 2023. 3. 31. 00:42
728x90

최초작성 : 2023.3.31

네이버 증권에서 일별 시세 정보를 가져오는 코드이다.

아래 코드로는 100% 만족된 결과를 도출하지 못한다.

IF 조건문을 사용해서 원하는 결과를 도출하는 건 개발자의 몫이다.

또한 DB에 저장되는 순서를 날짜가 적은 데이터를 먼저 저장하기 위한 방법으로 수정하는 것이 좋다.

 
import requests
from fake_useragent import UserAgent
# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
from bs4 import BeautifulSoup
# BeautifulSoup 에서는 Xpath 사용 불가능
import pymysql
import re
 
def financialSiseDayData(code,page):
    
    ua = UserAgent()
    headers = {'User-agent': ua.ie}
    url = f'https://finance.naver.com/item/sise_day.naver?code={code}&page={page}'
    # print(url)
 
    res = requests.get(url, headers=headers)
    #
    if res.status_code == 200:
        html = res.text
 
        # HTML 페이지 파싱 BeautifulSoup(HTML데이터, 파싱방법)
        soup = BeautifulSoup(html, 'html.parser')
 
        # find() : 가장 먼저 검색되는 태그 반환
        body = soup.select_one("table")
        # print(body)
 
        items = body.find_all('tr', {'onmouseover':'mouseOver(this)'})
        # print(items)
 
        conn = pymysql.connect(host="localhost", user="", password="", db="studydb", charset="utf8")
        curs = conn.cursor()
 
        for item in items:
            information = item.text
            info = information.split('\n')
            # print(info)
            # print(info[2], "type: ", type(info[2]))
            # print(code, info[1],info[2],info[8],info[9],info[10],info[11],info[5].strip())
            closeM = re.sub(r'[^0-9]''', info[2])
            openM = re.sub(r'[^0-9]''', info[8])
            highM = re.sub(r'[^0-9]''', info[9])
            lowM = re.sub(r'[^0-9]''', info[10])
            volume = re.sub(r'[^0-9]''', info[11])
 
            # 기존 DB에 있는 데이터인 경우에는 중복 저장하지 말고 무시하라.
            sql = "insert ignore into stock_day (code,date,close,open, high, low, volume) values (%s, %s, %s, %s, %s, %s, %s)"
            val = (str(code), info[1], closeM, openM, highM, lowM, volume)
            curs.execute(sql,val)
            conn.commit()
        conn.close()
 
    else:
        print(res.status_code)
 
 
if __name__ == '__main__':
    code = 140670
    for i in range(14): # 1개월 데이터만 추출
        financialSiseDayData(code,str(i))
 
    print('완료되었습니다.')

 

DB에 저장하기 위한 SQL 코드

CREATE TABLE stock_day (
  idx int(11NOT NULL,
  code varchar(8NOT NULL COMMENT '종목코드',
  date char(10NOT NULL COMMENT '날짜',
  close double NOT NULL DEFAULT 0 COMMENT '종가',
  open double NOT NULL DEFAULT 0 COMMENT '시가',
  high double NOT NULL DEFAULT 0 COMMENT '고가',
 low double NOT NULL DEFAULT 0 COMMENT '저가',
  volume double NOT NULL DEFAULT 0 COMMENT '거래량',
  display int(2NOT NULL DEFAULT 1,
  reg_date timestamp NOT NULL DEFAULT current_timestamp()
ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
ALTER TABLE stock_day
  ADD PRIMARY KEY (idx),
  ADD UNIQUE KEY code_date (code,date);
 
ALTER TABLE stock_day
  MODIFY idx int(11NOT NULL AUTO_INCREMENT;
COMMIT;

코드와 날짜를 UNIQUE KEY 로 설정하였다.

 

Updated : 2024.08.30

위 코드를 가지고 PHP simple_html_dom 라이브러리를 이용하는 코드로 변환을 시도했었다.

결과가 만족스럽지 못해서 DOM 을 분석하면서 다시 작성을 했다.

그리고 다시 ChatGTP를 이용하여 Python 코드로 변경해 달라고 했더니 좀 더 깔끔한 결과를 얻었다.

 

import requests
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
import re
 
def financialSiseDayData(code,page):
    ua = UserAgent()
    headers = {'User-agent': ua.ie}
    url = f'https://finance.naver.com/item/sise_day.naver?code={code}&page={page}'
    # print(url)
 
    res = requests.get(url, headers=headers)
    #
    if res.status_code == 200:
        html = res.text
 
        # HTML 페이지 파싱 BeautifulSoup(HTML데이터, 파싱방법)
        soup = BeautifulSoup(html, 'html.parser')
 
        # table = soup.select_one(".type2")
        # items = table.find_all('tr', {'onmouseover':'mouseOver(this)'})
 
        items = soup.select('.type2 tr[onmouseover="mouseOver(this)"]')
 
        stock_day_temp = []
        for item in items:
            info = []
            tds = item.find_all('td')
            for td in tds:
                info.append(td.get_text(strip=True))  # <td> 내부 텍스트를 추출하여 리스트에 저장
 
            sdate = re.sub(r'[^0-9.]''', info[0])  # 날짜
            closeM = re.sub(r'[^0-9]''', info[1])  # 종가
            openM = re.sub(r'[^0-9]''', info[3])  # 시가
            highM = re.sub(r'[^0-9]''', info[4])  # 고가
            lowM = re.sub(r'[^0-9]''', info[5])  # 저가
            volume = re.sub(r'[^0-9]''', info[6])  # 거래량
 
            cv30 = float(closeM) * 1.3  # 상한가
            cv60 = float(closeM) * 1.6  # 단기 투경 기준
            cv100 = float(closeM) * 2  # 4주 투경 기준
 
            stock_day_temp.append([
                code, sdate, closeM, openM, highM, lowM, volume, cv30, cv60, cv100
            ])
 
        for items in reversed(stock_day_temp):
            print(items)
 
    else:
        print(res.status_code)
 
 
if __name__ == '__main__':
    code = '039610'
    for i in range(1,3,1): # 1개월 데이터만 추출
        financialSiseDayData(code,str(i))
 
    print('완료되었습니다.')

 

 

 

728x90