Tech Blog

KRX에서 종목별 주가 데이터 크롤링하기 본문

etc.

KRX에서 종목별 주가 데이터 크롤링하기

agsu 2023. 3. 17. 09:56

Chapter 5 금융 데이터 수집하기 (기본)

이 블로그의 방법을 참고하여 진행하였습니다. 

 

1. KRX 데이터 크롤링 

1.1 필요 데이터셋 

 주가 빅데이터 관련 라이브러리 개발을 진행하면서 종목 별 주가 데이터가 필요했습니다. 주가 데이터를 불러올 수 있는 여러 라이브러리들이 있지만, 안정성 문제가 있어, 직접 크롤링하고 전처리하여 적재한 후 제공하기 위해 크롤링을 시도하게되었습니다. 

종목 별 날짜 별로 시가, 저가, 고가, 종가, 거래량 데이터가 필요하여 krx 정보데이터시스템에서 적절한 데이터를 찾아 진행하였습니다.  (데이터 링크: [KRX 정보데이터 시스템] 주식 - 종목시세 - 개별종목 시세 추이 )

 

 

1.2 크롤링 과정 

 여기서 사용한 방법은 데이터 다운로드 방법을 코드로 자동화 한 것이라고 할 수 있습니다. 데이터의 크롤링 과정을 요약하면 다음과 같습니다. 

 

[1단계] generate.cmd 의 요청 주소 ( http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd )에 .원하는 항목을 params의 formdata에 담아서 post 요청을 보내면, 그에 해당하는 OTP를 받습니다. 

[2단계] download.cmd 의 요청 주소 ( http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd )에 부여받은 OTP를 parms의 code에 post 요청을 보내면, 주가 데이터 파일이 다운로드됩니다.   

 

위 두단계의 과정을 자세히 알아보도록 하겠습니다. 

 

1.1.1 [1단계] otp code 부여받기 

먼저 수동으로 다운로드를 직접 해보며 생성되는 generate.cmd와 download.cmd를 확인해보겠습니다. 본 글에서는 csv 파일로 다운로드 받았습니다. 

 

그러면 개발자도구 [F12] 의 network 탭에 generate.cmd와 download.cmd가 생깁니다. 그 중 generate.cmd에 들어가서 Hearders에 있는 Request URL을 확인합니다. 해당 주소에 요청을 보내면 됩니다. 

 

그 다음, Payload에 있는 Form Data를 확인합니다. 원하는 데이터의 조건을 지정해서 Form Data로 보내주어야 합니다. 예를 들어 isuCd 를 보면 종목코드가 들어가 있고, strtDd, endDd에 원하는 날짜를 지정할 수 있습니다. 

 

두가지를 활용하여 OTP code를 발급받을 수 있는 파이썬 코드를 작성해보겠습니다. 

# generate.cmd 요청 주소 
otp_url ='http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd' 

# form data 
otp_form_data = {
  'locale': 'ko_KR',
  "share": '1',
  "csvxls_isNo": 'false',
  "name": 'fileDown',
  "url": 'dbms/MDC/STAT/standard/MDCSTAT01701',
  'strtDd': "20230323", # 다운로드 받고싶은 날짜 
  'endDd': "20230331",
  'adjStkPrc': 2, # 수정 주가 
  'adjStkPrc_check': 'Y', 
  'isuCd': "KR733626K013" 
}

# scraper 객체 
scraper = cloudscraper.create_scraper()

# form data와 함께 요청 
response = scraper.post(otp_url, params=otp_form_data)

# response의 text 에 담겨있는 otp 코드 가져오기  
otp = response.text

# 결과 확인 
print("result: ", response)
print("otp: ", otp)

 

 한 가지 유의 사항은, 크롤링 당시 requests 라이브러리가 불안정해서 cloudscraper라는 라이브러리로 대체하여 작성한 코드입니다. 

 

 

 

1.1.2 [2단계] 파일 다운로드 받기  

이제 위에서 받은 otp 코드를 download.cmd의 요청주소에 요청을 보내면 됩니다. 

download.cmd의 Request URL 을 확인하고, 

Payload의 form data에서 code 변수에 opt 코드를 받는 것을 확인하였습니다. 

 

이를 1단계에 이어 코드로 옮겨보도록 하겠습니다. 

csv_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd'
csv_form_data = scraper.post(csv_url, params={'code': otp})
csv_form_data.encoding = 'EUC-KR'
csv_form_data.text

원하는 데이터의 크롤링이 csv 파일로 잘 다운로드 된 것을 볼 수 있습니다. 

 

lst_row = []
for row in csv_form_data.text.split('\n'): 
    lst_row.append(row.split(','))

df = pd.DataFrame(lst_row[1:], columns=lst_row[0])
df

csv 데이터를 dataframe으로 변환하기 위해서 처리를 해준 코드입니다. 여기까지, KRX 정보데이터시스템에서 주가데이터를 크롤링해보았습니다. 이제 kospi, kosdaq, konex 시장의 여러 종목들에 대해서 한 번에 크롤링 할 수 있는 함수를 작성해보도록 하겠습니다. 

 

 

 

cf) 수정주가 

 한가지 참고할 점. 프로젝트를 진행하면서 액면분할에 따른 주가변동을 반영한 수정주가를 적용해야하는 문제가 있었습니다. 수정주가를 직접 계산하려던 찰나,  KRX 정보데이터시스템에서 수정종가를 반영할 수 있는 체크박스가 생겼습니다.

 그래서 수정주가 반영 조건을 넣어 다시 크롤링 하였습니다. 

 

 만약 수정주가 반영이 되지 않도록 크롤링 하고싶다면, 전에 generate.cmd의 form data에서 'adjStkPrc': 2,  'adjStkPrc_check': 'Y', 이 두가지 속성을 1, 'N'으로 변경해주면 됩니다. 

 

 

 

 

 

2. 최종 함수와 전처리 

 이제 모든 종목코드에 대하여 크롤링하고, 데이터프레임으로 합친 후에 csv 파일로 저장하는 코드를 작성해보겠습니다. 

 

표준 종목코드

통계 - KRX | 정보데이터시스템 (표준코드)

 

항상 6자리의 종목코드만 봐오다가 크롤링을 할 때 isuCd를 처음 봐서 조금 헤맸습니다. isuCd는 표준코드로, 12글자로 이루어져있습니다. 흔히 사용되는 종목코드는 가운데 6자리에 포함되어있습니다. 위에 있는 링크에서 다운받을 수 있습니다. 

import pandas as pd
dic_market2code = {}
stocks = pd.read_csv('stocks.csv', encoding = 'EUC-KR')
for market, data in stocks.groupby('시장구분'): 
    dic_market2code[market] = data['표준코드'].values

우리는 마켓별로 구분을 하여 주가 데이터를 다운로드 받을 것이므로, 마켓별 딕셔너리로 생성해주었습니다. 

 

 

최종 크롤링 함수 

위의 과정을 함수화 한 크롤링 최종 함수입니다. 

def krx_download(date:list, markets:list):
    import requests 
    import pandas as pd
    from bs4 import BeautifulSoup as bs
    from tqdm import tqdm
    import cloudscraper

    
    df_total = pd.DataFrame()
    for market in markets: # 마켓 별 저장 
        df_market = pd.DataFrame()
        lst_code = dic_market2code[market]
        for code in tqdm(lst_code): # 코드 별 데이터프레임 저장 
            otp_url ='http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd'
            otp_form_data = {
              'locale': 'ko_KR',
              "share": '1',
              "csvxls_isNo": 'false',
              "name": 'fileDown',
              "url": 'dbms/MDC/STAT/standard/MDCSTAT01701',
              'strtDd': date[0], # 다운로드 받고싶은 날짜 
              'endDd': date[1],
              'adjStkPrc': 2, # 수정주가 반영 
              'adjStkPrc_check': 'Y', 
              'isuCd': code 
            }
            
            scraper = cloudscraper.create_scraper()
            otp = scraper.post(otp_url, params=otp_form_data).text
            
            csv_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd'
            csv_form_data = scraper.post(csv_url, params={'code': otp})
            csv_form_data.encoding = 'EUC-KR'

            lst_row = []
            for row in csv_form_data.text.split('\n'): 
                lst_row.append(row.split(','))

            df = pd.DataFrame(lst_row[1:], columns=lst_row[0])
            df['Code'] = code 
            df_market = df_market.append(df)
        
        df_market['market'] = market
        df_total = df_total.append(df_market)
            

    df_total.to_csv(f'{date[0]}_{date[1]}.csv', index=None)

날짜와, 마켓을 인풋값으로 넣으면 시작날짜_종료날짜.csv 형태의 이름을 갖는 csv 데이터가 저장됩니다. 

krx_download(['20221101', '20221102'], ['KONEX', 'KOSPI'])

테스트로, KONEX, KOSPI에 대하여 2022년 11월 1일부터 2일까지 데이터를 생성해보았습니다. 

 

df = pd.read_csv('20221101_20221102.csv')
df

확인을 해보면, 크롤링이 성공적으로 된 것을 볼 수 있습니다. 

 

이 데이터에서 컬럼 추출, 데이터 타입 변경 등 필요에 따라 처리 하여 사용하면 좋을 것 같습니다.

 

태그를 사용해서 크롤링 해본 경험이 많았는데 다운로드의 과정을 코드로 작성해서 크롤링해보니 색달랐습니다. 네트워크 탭에서 요청주소를 확인하고, post 요청에 어떤 params를 담아보내야 하는지 직접 확인해 보며 다운로드 원리에 대하여 배울 수 있는 크롤링 과정이었습니다. 

 

 

 

Comments