주식 관련 데이터를 구하기 위해 가장 먼저 해야할 일은 어떤 종목들이 해당 국가 거래소에 상장되어있는가에 대한 정보를 구하는 것입니다. 우리나의 경우, 한국거래소에서 제공하는 업종분류 형황과 개별종목 지표 데이터를 이용하면, 매우 간단하게 해당 정보를 수집할 수 있습니다.
여기서는 먼저 업종분류 현황 데이터를 수집하는 방법을 다뤄보겠습니다.
- KRX 정보데이터시스템 http://data.krx.co.kr/ 에서 [기본통계 → 주식 → 세부안내] 부분
- [12025] 업종분류 현황: http://data.krx.co.kr/contents/MDC/MDI/mdiLoader/index.cmd?menuId=MDC0201020506
먼저 사이트에 접속한 뒤, 아래와 같이 CSV파일을 다운로드 받으면, 개발자 콘솔의 Network에 'generate.cmd'가 뜨는 걸 볼 수 있습니다. 이걸 클릭한 뒤, 'Hearders' 부분에서 Request URL을 미리 복사해준 뒤, 'Payload'를 클릭해줍니다.
그러면 아래와 같은 정보들을 확인할 수 있습니다. 이 정보를 Request URL에 제출하면 OTP를 받습니다. 이 OTP를 다시 download.cmd의 Request URL에 전송하면, 그에 해당하는 데이터가 다운로드 됩니다. 참고로 OTP 코드는 download.cmd의 Payload에서 확할 수 있습니다.
1. generate.cmd 원하는 항목 제출하여 OTP 코드 얻기
이 정보들을 활용하여 원하는 항목에 대한 데이터를 자동으로 불러오게 하기 위해 아래와 같이 코드를 작성해줍니다.
1. gen_otp_url 에 원하는 항목을 제출할 URL을 입력합니다.(generated.cmd의 Request URL)
2. 개발자도구 화면에 있는 쿼리 내용들을 딕셔너리 형태로 입력합니다. 이 중 mktId의 'STK'는 코스피에 해당하며, 코스닥 데이터를 받고자 할 경우 'KSQ'를 입력하면 됩니다.
3. 영업일을 뜻하는 trdDd에는 전 편에서 구한 최근 영업일 데이터를 입력합니다.
4. 헤더 부분에 리퍼러(Referer)를 추가합니다. 리퍼러란 링크를 통해서 각각의 웹사이트로 방문할 때 남는 흔적입니다. 거래소 데이터를 다운로드하는 과정을 살펴보면 첫 번째 URL에서 OTP를 부여받고, 이를 다시 두번째 URL에 제출합니다. 그런데 이러한 과정의 흔적이 없이 OTP를 바로 두번째 URL에 제출하면 서버는 이를 로봇으로 인식해 데이터를 주지 않습니다. 따라서 헤더 부분에 우리가 거쳐온 과정을 흔적으로 남겨야 데이터를 받을 수 있습니다. 이러한 리퍼러 주소는 개발자도구 화면에서도 확인할 수 있습니다.
5. post() 함수를 통해 해당 URL에 쿼리를 전송하면 이에 해당하는 데이터를 받으며, 이 중 텍스트에 해당하는 내용만 불러옵니다.
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'
}
headers = {'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'}
otp_stk = rq.post(gen_otp_url, gen_otp_stk, headers=headers).text
# 결과: download.cmd의 Payload에 나와있는 OTP 코드
2. download.cmd OTP 코드 제출하여 CSV 파일 불러오기
이제 이 OTP 코드를 활용하여, CSV 파일을 다운로드 받아보겠습니다. post() 함수를 통해 download.cmd의 Request URL에 OTP 코드 그리고 위에서 만들어 놓은 headers를 제출합니다. 그 후에 받은 데이터의 content 부분을 BytestIO()를 이용해 바이너리스트림 형태로 만든 후, read_csv() 함수를 통해 데이터를 읽어오면 됩니다. 해당 데이터는 EUR-KR 형태로 인코딩 되어있으므로, 인코딩도 해줍니다.
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) #결과: <Response [200]>
sector_stk = pd.read_csv(BytesIO(down_sector_stk.content), encoding='EUC-KR')
sector_stk.head()
그러면 아래와 같이 KRX 정보데이터시스템의 업종분류 현황에 있는 데이터와 동일한 데이터가 불러와 짐을 알 수 있습니다.
위 과정을 똑같이 응용하여, generated.cmd의 payload에서 mktId부분만 'STK'에서 'KSQ'로 바꿔서 코스닥 데이터도 불러와 줍니다.
gen_otp_ksq = {
'mktId': 'KSQ', # 코스닥 입력
'trdDd': biz_day,
'money': '1',
'csvxls_isNo': 'false',
'name': 'fileDown',
'url': 'dbms/MDC/STAT/standard/MDCSTAT03901'
}
otp_ksq = rq.post(gen_otp_url, gen_otp_ksq, headers=headers).text
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')
sector_ksq.head()
이제 코스피, 코스닥 데이터를 concat() 함수를 통해 하나로 합치고, 인덱스도 초기화해줍니다. 종목명에 공백이 있는 경우가 있으므로, strop() 메서드로 제거해줍니다. 마지막으로 언제 추출한 데이터인지를 구분하기 위해, 데이터의 기준일에 해당하는 '기준일' 열도 추가해줍니다.
krx_sector = pd.concat([sector_stk, sector_ksq]).reset_index(drop=True)
krx_sector['종목명'] = krx_sector['종목명'].str.strip()
krx_sector['기준일'] = biz_day
krx_sector.head()
전체 코드
import pandas as pd
import requests as rq
from io import BytesIO
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'
}
headers = {'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'}
otp_stk = rq.post(gen_otp_url, gen_otp_stk, headers=headers).text
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)
sector_stk = pd.read_csv(BytesIO(down_sector_stk.content), encoding='EUC-KR')
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',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'}
otp_ksq = rq.post(gen_otp_url, gen_otp_ksq, headers=headers).text
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 # 기준일 열 추가
참고자료
https://github.com/hyunyulhenry/quant_py/blob/main/data_korea.ipynb
다음편
'데이터분석 > 크롤링' 카테고리의 다른 글
파이썬 퀀트투자(5): WICS 기준 섹터정보 크롤링, 적재 (2) | 2024.10.12 |
---|---|
파이썬 퀀트투자(3): 한국거래소 개별종목 지표 크롤링 (3) | 2024.10.09 |
파이썬 퀀트투자(1): 최근 영업일 기준 데이터 크롤링 (7) | 2024.10.09 |
BeautifulSoup을 이용한 정적 크롤링 개념 정리 및 실습 (0) | 2024.03.15 |
파이썬 웹 크롤링 환경세팅(VScode, miniconda, selenium) 및 예시코드 (0) | 2023.12.10 |
댓글