-
[코딩_05] 종목명으로 KRX에서 주가정보 가져오기개발일지 2022. 12. 27. 00:24반응형
종목명으로 주가 정보 가져오기 코딩_03, 코딩_04를 거쳐 드디어 코딩_05이다.
(이전 글이 보고 싶다면 하단의 더 보기를 눌러서 확인하면 된다.)
더보기2022.10.01 - [개발일지] - [코딩_03] KRX 정보데이터시스템에서 전종목 기본정보 가져오기 (KRX 크롤링)
[코딩_03] KRX 정보데이터시스템에서 전종목 기본정보 가져오기 (KRX 크롤링)
안녕하세요. 비주얼 스튜디오 코드 설치와 확장 프로그램 설치까지 끝났다면, 이제는 본격적으로 코딩을 해보아야겠죠? 오늘은 'KRX 정보데이터시스템'에서 전종목 기본정보를 크롤링해보려 합
always-good.tistory.com
2022.10.09 - [개발일지] - [코딩_04] 크롤링한 종목정보를 이용해 종목명으로 표준코드, 종목코드 찾기
[코딩_04] 크롤링한 종목정보를 이용해 종목명으로 표준코드, 종목코드 찾기
안녕하세요. 지난번에는 'KRX 정보 데이터 시스템'에서 전종목 기본정보를 크롤링해 엑셀에 저장해보았습니다. 2022.10.01 - [개발일지] - [코딩_03] KRX 정보 데이터 시스템에서 전종목 기본정보 가져
always-good.tistory.com
처음부터 목표로 했던 내용이 '종목명으로 KRX에서 주가 정보를 불러오는 것'이었다.
이번 글을 보면 모두들 VS코드에서 종목명으로 원하는 기간만큼의 주가 정보를 저장할 수 있을 것이다.
그러면 바로 시작해 보자.
0. 전체 코드 보기
전체 코드 내용만 확인하고 싶을 경우 더 보기를 눌러서 확인하면 된다.
더보기import requests import openpyxl import datetime import time import pandas as pd from pandas import DataFrame, Series from io import BytesIO from tqdm import tqdm from openpyxl.utils.dataframe import dataframe_to_rows def mk_all_stock_info(): """ KRX에서 제공하는 전종목 기본정보 중 '표준코드', '종목코드', '한글 종목약명'을 데이터프레임에 저장 return: 데이터프레임(한글종목약명(index), 표준코드, 종목코드 ) """ # generate.cmd > Headers > General > Request URL 주소 gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd' # generate.cmd > Payload > Form Data 내용 gen_otp_data = { 'locale': 'ko_KR', 'mktId': 'ALL', 'share': 1, 'csvxls_isNo': 'false', 'name': 'fileDown', 'url': 'dbms/MDC/STAT/standard/MDCSTAT01901' } # 로봇으로 인식하지 않게 하기 위함 headers = {'Referer' : 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'} otp = requests.post(gen_otp_url, params=gen_otp_data, headers=headers).text # download.cmd > Headers > General > Request URL 주소 down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd' down_data = { 'code' : otp } # requests로 data 전달 down_stock_info = requests.post(down_url, params=down_data, headers=headers) stock_info = pd.read_csv(BytesIO(down_stock_info.content), encoding='EUC-KR', index_col='한글 종목약명') return stock_info def find_stock_std_code(name, stock_info): """ 종목명을 기준으로 표준코드 반환 Args: name (string): 표준코드를 찾고자 하는 종목의 종목명 (한글 종목약명) stock_info (dataframe): 한글 종목약명(index), 표준코드, 단축코드를 갖는 데이터프레임 Returns: string: 찾고자 하는 종목의 표준코드 """ try: stock_std_code = stock_info.loc[name, '표준코드'] return stock_std_code except KeyError: print('종목명이 잘못되었습니다') except: print('error') def find_stock_code(name, stock_info): """ 종목명을 기준으로 단축코드 반환 Args: name (string): 표준코드를 찾고자 하는 종목의 종목명 (한글 종목약명) stock_info (dataframe): 한글 종목약명(index), 표준코드, 단축코드를 갖는 데이터프레임 Returns: string: 찾고자 하는 종목의 단축코드 """ try: stock_code = stock_info.loc[name, '단축코드'] return stock_code except KeyError: print('종목명이 잘못되었습니다') except: print('error') def stock_price_data(name, std_code, code, info, start_day, end_day): """ 특정 기간동안의 주가 정보를 데이터프레임 형태로 저장 Args: name (string): 종목명 std_code (string): 표준코드 code (string): 단축코드 info (list): 원하는 정보 start_day: 시작날짜 (ex: 20220103) end_day : 종료날짜 (ex: 20220928) Returns: dataframe : 일자(index), 종가 """ gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd' gen_otp_data = { 'locale': 'ko_KR', 'tboxisuCd_finder_stkisu0_0': code + '/' + name, 'isuCd': std_code, 'isuCd2': code, 'codeNmisuCd_finder_stkisu0_0': name, 'param1isuCd_finder_stkisu0_0': 'ALL', 'strtDd': start_day, 'endDd': end_day, 'share': '1', 'money': '1', 'csvxls_isNo': 'false', 'name': 'fileDown', 'url': 'dbms/MDC/STAT/standard/MDCSTAT01701', } headers = {'Referer' : 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'} otp = requests.post(gen_otp_url, gen_otp_data, headers=headers).text down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd' down_stock_price = requests.post(down_url, {'code':otp}, headers=headers) stock_price = pd.read_csv(BytesIO(down_stock_price.content), usecols=info, encoding='EUC-KR', index_col='일자') return stock_price def new_excel_save(save_path, file_name, sheet_name, data): """ 경로, 파일명, 시트명을 주면 새로운 엑셀 파일을 만들어 저장 Args: save_path (string): 엑셀 저장 경로 file_name (string): 엑셀 파일명 sheet_name (string): 엑셀 시트명 data (dataframe): 엑셀에 저장하고 싶은 데이터프레임 """ save_fpath = save_path + file_name + '.xlsx' wb = openpyxl.Workbook() wb.save(save_fpath) # 엑셀 오픈 wb = openpyxl.load_workbook(save_fpath) wb.create_sheet(sheet_name) del wb['Sheet'] ws = wb[sheet_name] if type(data) == DataFrame: for r in dataframe_to_rows(data): ws.append(r) ws['A1'].value = ws['A2'].value ws.delete_rows(2) wb.save(save_fpath) elif type(data) == Series: data = pd.DataFrame(data) for r in dataframe_to_rows(data): ws.append(r) ws['A1'].value = ws['A2'].value ws.delete_rows(2) wb.save(save_fpath) else: print('Data 타입이 잘못되었습니다.') wb.close() # 검색하고 싶은 정보 입력 company = ['삼성전자', 'SK하이닉스'] stock_name = company stock_info = ['일자', '종가'] start_day = '20100103' end_day = '20221014' save_fpath = '원하는 경로 설정' excel_name = 'test' excel_sheet_name = '주가' # 모든 종목의 기본 정보를 불러옴 all_stock_info = mk_all_stock_info() # 잘못된 종목명 있는지 확인 for name in stock_name: if (name in all_stock_info.index) == True: pass else: print('잘못된 종목명을 입력하셨습니다. ' + name) # 단일종목의 주가를 저장하는 경우 if len(stock_name) == 1: std_code = find_stock_std_code(stock_name, all_stock_info) code = find_stock_code(stock_name, all_stock_info) temp_data = stock_price_data(stock_name, std_code, code, stock_info, start_day, end_day) new_excel_save(save_fpath, excel_name, excel_sheet_name, temp_data) # 여러종목의 주가를 저장하는 경우 elif len(stock_name) != 1: std_code = find_stock_std_code(stock_name[0], all_stock_info) code = find_stock_code(stock_name[0], all_stock_info) temp_df = stock_price_data(stock_name[0], std_code, code, stock_info, start_day, end_day) temp_df.columns = [stock_name[0]] for name in stock_name[1:]: std_code = find_stock_std_code(name, all_stock_info) code = find_stock_code(name, all_stock_info) temp_data = stock_price_data(name, std_code, code, stock_info, start_day, end_day) temp_data.columns = [name] temp_df = pd.concat([temp_df, temp_data], axis=1) new_excel_save(save_fpath, excel_name, excel_sheet_name, temp_df)
1. 라이브러리 및 함수 임포트
이번에도 시작은 라이브러리 import 및 이전에 작성해두었던 함수를 가져오는 것이다.
이것저것 라이브러리가 많은데, 사실 대부분 이전에 사용한 라이브러리들이다.
라이브러리 Import
import requests import openpyxl import datetime import time import pandas as pd from pandas import DataFrame, Series from io import BytesIO from tqdm import tqdm from openpyxl.utils.dataframe import dataframe_to_rows
이전에 작성한 함수 가져오기
def mk_all_stock_info(): """ KRX에서 제공하는 전종목 기본정보 중 '표준코드', '종목코드', '한글 종목약명'을 데이터프레임에 저장 return: 데이터프레임(한글종목약명(index), 표준코드, 종목코드 ) """ # generate.cmd > Headers > General > Request URL 주소 gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd' # generate.cmd > Payload > Form Data 내용 gen_otp_data = { 'locale': 'ko_KR', 'mktId': 'ALL', 'share': 1, 'csvxls_isNo': 'false', 'name': 'fileDown', 'url': 'dbms/MDC/STAT/standard/MDCSTAT01901' } # 로봇으로 인식하지 않게 하기 위함 headers = {'Referer' : 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'} otp = requests.post(gen_otp_url, params=gen_otp_data, headers=headers).text # download.cmd > Headers > General > Request URL 주소 down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd' down_data = { 'code' : otp } # requests로 data 전달 down_stock_info = requests.post(down_url, params=down_data, headers=headers) stock_info = pd.read_csv(BytesIO(down_stock_info.content), encoding='EUC-KR', index_col='한글 종목약명') return stock_info def find_stock_std_code(name, stock_info): """ 종목명을 기준으로 표준코드 반환 Args: name (string): 표준코드를 찾고자 하는 종목의 종목명 (한글 종목약명) stock_info (dataframe): 한글 종목약명(index), 표준코드, 단축코드를 갖는 데이터프레임 Returns: string: 찾고자 하는 종목의 표준코드 """ try: stock_std_code = stock_info.loc[name, '표준코드'] return stock_std_code except KeyError: print('종목명이 잘못되었습니다') except: print('error') def find_stock_code(name, stock_info): """ 종목명을 기준으로 단축코드 반환 Args: name (string): 표준코드를 찾고자 하는 종목의 종목명 (한글 종목약명) stock_info (dataframe): 한글 종목약명(index), 표준코드, 단축코드를 갖는 데이터프레임 Returns: string: 찾고자 하는 종목의 단축코드 """ try: stock_code = stock_info.loc[name, '단축코드'] return stock_code except KeyError: print('종목명이 잘못되었습니다') except: print('error') def new_excel_save(save_path, file_name, sheet_name, data): """ 경로, 파일명, 시트명을 주면 새로운 엑셀 파일을 만들어 저장 Args: save_path (string): 엑셀 저장 경로 file_name (string): 엑셀 파일명 sheet_name (string): 엑셀 시트명 data (dataframe): 엑셀에 저장하고 싶은 데이터프레임 """ save_fpath = save_path + file_name + '.xlsx' wb = openpyxl.Workbook() wb.save(save_fpath) # 엑셀 오픈 wb = openpyxl.load_workbook(save_fpath) wb.create_sheet(sheet_name) del wb['Sheet'] ws = wb[sheet_name] if type(data) == DataFrame: for r in dataframe_to_rows(data): ws.append(r) ws['A1'].value = ws['A2'].value ws.delete_rows(2) wb.save(save_fpath) elif type(data) == Series: data = pd.DataFrame(data) for r in dataframe_to_rows(data): ws.append(r) ws['A1'].value = ws['A2'].value ws.delete_rows(2) wb.save(save_fpath) else: print('Data 타입이 잘못되었습니다.') wb.close()
2. 주가 정보 크롤링 함수 만들기
라이브러리 Import 및 함수 입력이 끝났다면 본격적으로 주가 정보 크롤링 함수를 만들 차례이다.
더보기def stock_price_data(name, std_code, code, info, start_day, end_day): """ 특정 기간동안의 주가 정보를 데이터프레임 형태로 저장 Args: name (string): 종목명 std_code (string): 표준코드 code (string): 단축코드 info (list): 원하는 정보 start_day: 시작날짜 (ex: 20220103) end_day : 종료날짜 (ex: 20220928) Returns: dataframe : 일자(index), 종가 """ gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd' gen_otp_data = { 'locale': 'ko_KR', 'tboxisuCd_finder_stkisu0_0': code + '/' + name, 'isuCd': std_code, 'isuCd2': code, 'codeNmisuCd_finder_stkisu0_0': name, 'param1isuCd_finder_stkisu0_0': 'ALL', 'strtDd': start_day, 'endDd': end_day, 'share': '1', 'money': '1', 'csvxls_isNo': 'false', 'name': 'fileDown', 'url': 'dbms/MDC/STAT/standard/MDCSTAT01701', } headers = {'Referer' : 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'} otp = requests.post(gen_otp_url, gen_otp_data, headers=headers).text down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd' down_stock_price = requests.post(down_url, {'code':otp}, headers=headers) stock_price = pd.read_csv(BytesIO(down_stock_price.content), usecols=info, encoding='EUC-KR', index_col='일자') return stock_price
1. 함수 선언
def stock_price_data(name, std_code, code, info, start_day, end_day): """ 특정 기간동안의 주가 정보를 데이터프레임 형태로 저장 Args: name (string): 종목명 std_code (string): 표준코드 code (string): 단축코드 info (list): 원하는 정보 start_day: 시작날짜 (ex: 20220103) end_day : 종료날짜 (ex: 20220928) Returns: dataframe : 일자(index), 종가 """
2. KRX정보데이터 시스템 접속 > 종목시세 > 개별종목 시세 추이 페이지로 이동
KRX정보데이터 접속 및 페이지 이동 KRX 정보데이터 페이지에 접속해 준다.
KRX 정보데이터: https://data.krx.co.kr/
정보데이터시스템
data.krx.co.kr
메인페이지 좌측을 보면 여러 메뉴가 있는데 '종목시세' > '개별종목 시세 추이' 페이지로 이동한다.
3. 원하는 종목명을 입력 후 CSV로 다운
종목 주가 정보 CSV 다운로드 개별종목 시세 추이 페이지로 이동했다면,
주가 정보를 확인하고 싶은 종목명과 기간을 입력해 준다.
이후 빨간색 1번, 2번을 따라서 CSV 파일을 다운로드해 보자.
4. F12를 눌러 generate.cmd 정보 확인
generate.cmd 정보 확인 크롤링을 위해 필요한 정보들을 수집할 차례이다.
컴퓨터에게는 친절하게 한 땀 한 땀 설명을 해줘야 우리의 일을 대신해 준다.
즉, 크롤링할 URL 주소와 방법, 어떤 정보들을 가져와야 하는지를 말해줘야 한다.
이런 정보들은 generate.cmd에서 확인할 수 있으며, 코드로 구현하면 다음과 같다.
1. generate.cmd > Headers > General을 들어가 Request URL이라는 것을 복사해 변수로 설정한다.
2. generate.cmd > Payload에 들어가 Form Data도 복사해 변수로 설정한다.
3. Post 방식을 활용해 requests를 진행한다.
gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd' gen_otp_data = { 'locale': 'ko_KR', 'tboxisuCd_finder_stkisu0_0': code + '/' + name, 'isuCd': std_code, 'isuCd2': code, 'codeNmisuCd_finder_stkisu0_0': name, 'param1isuCd_finder_stkisu0_0': 'ALL', 'strtDd': start_day, 'endDd': end_day, 'share': '1', 'money': '1', 'csvxls_isNo': 'false', 'name': 'fileDown', 'url': 'dbms/MDC/STAT/standard/MDCSTAT01701', } headers = {'Referer' : 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader'} otp = requests.post(gen_otp_url, gen_otp_data, headers=headers).text
5. F12를 눌러 download.cmd 정보 확인
download.cmd 정보 확인 이번에는 download.cmd에 있는 정보를 불러올 것이다.
어려울 것 없다. generate.cmd보다 더 쉽다.
1. download.cmd > Headers > General을 들어가 Request URL이라는 것을 복사해 변수로 설정한다.
2. Post 방식을 활용해 requests를 진행한다.
down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd' down_stock_price = requests.post(down_url, {'code':otp}, headers=headers)
6. 크롤링한 정보를 dataframe으로 저장 후 Return
이제 download.cmd의 정보를 데이터프레임 형태로 저장 후 Return 해준다.
stock_price = pd.read_csv(BytesIO(down_stock_price.content), usecols=info, encoding='EUC-KR', index_col='일자') return stock_price
3. 주가 정보 크롤링 함수 실행
이제 크롤링을 위한 거의 모든 준비가 끝났다.
함수를 만들었으니 직접 실행해보면서 작동이 잘 되는지 확인을 해야 하는데,
한 가지 고민해볼 일이 있다.
바로 주가정보를 한 종목씩 가져올 것인가, 여러 종목을 한 번에 가져올 것인가에 대해 고민을 해봐야 한다.
당연히 나는 여러 종목을 한 번에 가져오는 것도 고려하는 게 좋다고 생각을 한다.
그렇기에 if문을 통해 경우의 수를 구분해서 엑셀로 저장할 데이터프레임을 만들어야 한다.
순서는 다음과 같다.
1. 검색하고 싶은 기본 정보들을 정리한다.
2. 모든 종목의 기본 정보를 불러온다.
3. 주가 정보를 불러오는 것이 단일 종목인지 여러 종목인지 경우의 수를 구분해 함수를 호출한다.
company = ['삼성전자', 'SK하이닉스'] # 검색하고 싶은 정보 입력 stock_name = company stock_info = ['일자', '종가'] start_day = '20100103' end_day = '20221014' save_fpath = '사용자가 원하는 경로' excel_name = 'test' excel_sheet_name = '주가' # 모든 종목의 기본 정보를 불러옴 all_stock_info = mk_all_stock_info() # 잘못된 종목명 있는지 확인 for name in stock_name: if (name in all_stock_info.index) == True: pass else: print('잘못된 종목명을 입력하셨습니다. ' + name) # 단일종목의 주가를 저장하는 경우 if len(stock_name) == 1: std_code = find_stock_std_code(stock_name, all_stock_info) code = find_stock_code(stock_name, all_stock_info) temp_data = stock_price_data(stock_name, std_code, code, stock_info, start_day, end_day) new_excel_save(save_fpath, excel_name, excel_sheet_name, temp_data) # 여러종목의 주가를 저장하는 경우 elif len(stock_name) != 1: std_code = find_stock_std_code(stock_name[0], all_stock_info) code = find_stock_code(stock_name[0], all_stock_info) temp_df = stock_price_data(stock_name[0], std_code, code, stock_info, start_day, end_day) temp_df.columns = [stock_name[0]] for name in stock_name[1:]: std_code = find_stock_std_code(name, all_stock_info) code = find_stock_code(name, all_stock_info) temp_data = stock_price_data(name, std_code, code, stock_info, start_day, end_day) temp_data.columns = [name] temp_df = pd.concat([temp_df, temp_data], axis=1) new_excel_save(save_fpath, excel_name, excel_sheet_name, temp_df)
이상으로 종목명으로 KRX에서 주가정보 가져오기를 해보았다.
어렵고 복잡해 보일 수 있지만 사실 특별히 어려울 일이 없다고 생각한다.
모두들 따라 할 수 있으니 실제로 사용해보고 실제 투자에 도움이 되었으면 한다.
반응형'개발일지' 카테고리의 다른 글
[코딩_04] 크롤링한 종목정보를 이용해 종목명으로 표준코드, 종목코드 찾기 (0) 2022.10.09 [코딩_03] KRX 정보데이터시스템에서 전종목 기본정보 가져오기 (KRX 크롤링) (2) 2022.10.01 [코딩_02] 비주얼 스튜디오 코드 확장 프로그램 설치 (0) 2022.09.29 [코딩_01] 비주얼 스튜디오 코드(Visual Studio Code) 설치 (0) 2022.09.13