본문 바로가기

파이썬으로 퀀트 프로그램 만들기 project

국내 주식 티커 데이터 크롤링하기_1

728x90

주식 데이터 크롤링에 앞서 가장 먼저 해야할 것은 최근 영업일 기준 데이터를 크롤링 하는 것입니다.

 

#최근 영업일 데이터 크롤링

[네이버 금융] -> [국내증시] -> [증시자금동향] 접속(링크)

여기서 이전 2영업일 날짜가 나타납니다. 날짜는 네이버에서 자동으로 업데이트 해주므로 해당 부분을 크롤링 한 후 

날짜에 해당하는 쿼리항목에 사용하면 됩니다.

날짜 데이터 크롤링

f12를 눌르고 날짜부분을 검사해 보면 해당 부분의 html 위치를 알 수 있습니다.

[div태그 "subtop_sise_graph2" 클래스] -> [u1 태그 "subtop_chart_note" 클래스] -> [li 태그] -> [span 태그 "tah"클래스]

이것을 통해 날짜 데이터를 뽑아내겠습니다.

###### 최근 영업일 기준 데이터 받기 #####
import requests as rq
from bs4 import BeautifulSoup
import re

url = 'https://finance.naver.com/sise/sise_deposit.naver'
data = rq.get(url)
data_html = BeautifulSoup(data.content, "html.parser")

#똑같은 부분이 여러개 위치할 때는 select_one() 사용할 것 -> 맨 위 데이터만 선택
parse_day = data_html.select_one(
    'div.subtop_sise_graph2 > ul.subtop_chart_note > li > span.tah'
).text

#숫자만 뽑아내기
biz_day = re.findall('[0-9]+', parse_day)
biz_day = ''.join(biz_day)
print(biz_day)

 

 

#티커 데이터 크롤링 하기

한국 거래소에서 제공하는 업종분류 현황과 개별종목 지표 데이터를 이용하겠습니다.

krx 정보데이터 시스템 사이트를 이용하겠습니다. 

여기서 업종 분류 현황은 [기본통계] -> [주식] -> [세부안내] -> [업종분류 현황]

 

해당 페이지로 이동하면 종목들 데이터를 excel이나 csv 파일로 다운 받을 수 있는데,

매번 사용할 때마다 이것들을 다운받는 것은 매우 번거롭습니다.

저희는 크롤링을 이용하여 해당 데이터를 파이썬에서 바로 불러오도록 하겠습니다.

우선 해당 페이지에서 f12를 눌러주시고 network를 눌러주세요

그리고 csv파일 다운 버튼을 눌러주세요 그러면 아래의 이미지 같이 generate.cmd와 download.cmd가 뜹니다.

 

 

#데이터를 다운받는 과정

거래소에서 엑셀이나 csv파일을 다운 받는 과정을 다음과 같습니다.

generate.cmd를 클릭하시고 headers를 보시면 requeset url이 있습니다.

이 request url에 원하는 항목의 쿼리를 제출하면 OTP를 받을 수 있습니다.

여기서의 쿼리는 Payload탭에 Form Data 부분이 쿼리에 해당합니다.

이 쿼리를 request url에 보내면 OTP를 받을 수 있습니다.

이 받은 OTP는 download.cmd에 hearders에 request url에 다시 보냅니다.

받은 OTP를 확인하고 싶으면 download.cmd에 payload탭을 보시면 됩니다.

이 OTP를 request url에 보내면 우리가 원하는 데이터 파일을 다운받을 수 있습니다.

이 과정들을 python에서 해보겠습니다!

 

 

#python 실습

###### 티커 크롤링 #####
import request as rq
from io import BytesIO
import pandas as pd

##코스피##
#post 방식이므로 url과 쿼리가 필요
#url은 generate.cmd의 request url, 쿼리는 Payload탭의 Form data 부분
gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd'
gen_otp_stk = {
    'mktId': 'STK',
    'trdDd': biz_day, #위에서 구한 최근 영업일
    'money': '1',
    'csvxls_isNo': 'false',
    'name': 'fileDown',
    'url': 'dbms/MDC/STAT/standard/MDCSTAT03901'
}

#리퍼러를 남겨서 웹사이트 방문 흔적을 남겨야 데이터를 받을 수 있음
#generate.cmd에서 referer 부분을 복붙하면 됨
headers = {'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'}

#otp 발급
otp_stk = rq.post(gen_otp_url, gen_otp_stk, headers=headers).text

#download.cmd의 request URL부분
down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd'

#파일 다운
down_sector_stk = rq.post(down_url, {'code': otp_stk}, headers=headers)

#csv의 html버전
#print(down_sector_stk.content)

#BytestIO를 이용해 바이너리스트림 형태로 변경
#한국 거래소 데이터는 EUC-KR 형태로 인코딩 되어있음
#read_csv 함수는 디폴트로 UTF-8 인코딩이라서 EUC-KR로 바꿔줘야함
sector_stk = pd.read_csv(BytesIO(down_sector_stk.content), encoding = 'EUC-KR')

 

위의 코드는 코스피 엑셀 파일을 다운받은 것이고 이제는 코스닥 파일을 다운받아 보겠습니다.

위에서 'stk' 부분을 모두 'ksq'로 바꿔 주시면 됩니다.

 

##코스닥##
gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd'
gen_otp_ksq = {
    'mktId': 'KSQ',
    'trdDd': biz_day, #위에서 구한 최근 영업일
    'money': '1',
    'csvxls_isNo': 'false',
    'name': 'fileDown',
    'url': 'dbms/MDC/STAT/standard/MDCSTAT03901'
}

headers = {'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'}

#otp 발급
otp_ksq = rq.post(gen_otp_url, gen_otp_ksq, headers=headers).text

#download.cmd의 request URL부분
down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd'

#파일 다운
down_sector_ksq = rq.post(down_url, {'code': otp_ksq}, headers=headers)

sector_ksq = pd.read_csv(BytesIO(down_sector_ksq.content), encoding = 'EUC-KR')

 

이제 두 파일을 합치고 클랜징 해보겠습니다.

#코스피 코스닥 합치기
krx_sector = pd.concat([sector_stk, sector_ksq]).reset_index(drop=True)

#종목명에 있는 공백 제거
krx_sector['종목명'] = krx_sector['종목명'].str.strip()

#기준일 추가
krx_sector['기준일'] = biz_day

print(krx_sector)

 

 

#개별 종목 지표 크롤링하기

이번에는 [주식] -> [세부안내] -> [PER/PBR/배당수익률(개별종목)] 으로 이동해주세요.

그리고 이전과 똑같이 f12 켜주시고 네트워크 눌러주시고 csv파일 다운받아 주시면 generate.cmd와 download.cmd가 보이실 겁니다. 그후 파이썬에서 똑같이 해주시면 됩니다. payload부분만 조금 수정되겠네요.

gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd'
gen_otp_data = {
    'searchType': '1',
    'mktId': 'ALL',
    'trdDd': biz_day,
    'name': 'fileDown',
    'url': 'dbms/MDC/STAT/standard/MDCSTAT03501'
}

headers = {'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'}

#otp 발급
otp_data = rq.post(gen_otp_url, gen_otp_data, headers=headers).text

#download.cmd의 request URL부분
down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd'

#파일 다운
krx_ind = rq.post(down_url, {'code': otp_data}, headers=headers)

krx_ind = pd.read_csv(BytesIO(krx_ind.content), encoding = 'EUC-KR')

#종목명에 있는 공백 제거
krx_ind['종목명'] = krx_ind['종목명'].str.strip()

#기준일 추가
krx_ind['기준일'] = biz_day

마지막에 기준일을 추가해줬습니다.

 

 

#데이터 합치기

#종목 지표와 섹터 데이터 합치기
#기준이 되는 열로 합쳐야 함 -> on 뒤에 있는 것들이 기준
kor_ticker = pd.merge(krx_sector,
                      krx_ind, 
                      on=krx_sector.columns.intersection(
                          krx_ind.columns).tolist(), how= 'outer'
                      )

 

 

#종목 구분하기

## 일반적인 종목과 스팩, 우선주, 리츠, 기타 주식을 구분 ##
#스팩 종목
kor_ticker[kor_ticker['종목명'].str.contains('스펙|제[0-9]+호')]['종목명']

#우선주
#국내 종목 중 종목코드 끝이 0이면 보통주, 0이 아니면 우선주
kor_ticker[kor_ticker['종목코드'].str[-1:] != '0']['종목명']

#리츠
#'리츠'로 끝나는 종목명
kor_ticker[kor_ticker['종목명'].str.endswith('리츠')]

 

 

#종목구분 열 추가

import numpy as np

#두 데이터에 공통으로 존재하지 않는 종목 찾기
#symmetric_difference()는 하나의 집함에만 존재하는 부분을 찾음
diff = list(set(krx_sector['종목명']).symmetric_difference(set(krx_ind['종목명'])))

kor_ticker['종목구분'] = np.where(kor_ticker['종목명'].str.contains('스펙|제[0-9]+호'), '스펙',
                              np.where(kor_ticker['종목코드'].str[-1:] != '0', '우선주',
                                       np.where(kor_ticker['종목명'].str.endswith('리츠'), '리츠',
                                                np.where(kor_ticker['종목명'].isin(diff), '기타',
                                                         '보통주'))))

 

 

#마지막 데이터 클랜징

kor_ticker = kor_ticker.reset_index(drop=True)
kor_ticker.columns = kor_ticker.columns.str.replace(' ','')
kor_ticker = kor_ticker[['종목코드', '종목명', '시장구분', '종가', '시가총액',
                         '기준일', 'EPS', '선행EPS', 'BPS', '주당배당금', '종목구분']]
kor_ticker = kor_ticker.replace({np.nan: None})