GIL's LAB

아이투자에서 투자지표 (EPS, PER, 영업이익률 등) 크롤링하기 본문

퀀트 투자/데이터 수집

아이투자에서 투자지표 (EPS, PER, 영업이익률 등) 크롤링하기

GIL~ 2021. 9. 8. 09:30

본 포스팅에서는 투자시에 자주 참고하게 되는 EPS, PER, 영업이익률 등의 투자지표를 크롤링하는 방법을 소개한다.

 

먼저, 아이투자(http://www.itooza.com/)에 접속하자.

접속하면 아래와 같은 화면을 볼 수 있다. 빨간 박스로 표시한 검색창에 관심있는 종목명 혹은 종목코드를 검색해보도록 하자. 여기서는 예제로 SK하이닉스를 검색하였다.

 

검색하면 아래와 같은 화면을 볼 수 있다.

보다시피, SK하이닉스의 다양한 투자지표가 테이블 형태로 정리된 것을 볼 수 있다.

본 포스팅에서는 저 테이블을 크롤링할 것이다. 다만, 투자지표가 컬럼명으로 오는 것이 추후 분석에 편할 것 같아 전치를 시켜서 저장하도록 한다. 

 

자, 그러면 이제 파이썬으로 저 테이블을 크롤링해보자.

먼저 필요한 모듈을 다음과 같이 임포트한다. 

사실 time 모듈과 fdr 모듈은 전체 종목을 크롤링할때만 필요하지만, 미리 임포트해두자.

import requests
from bs4 import BeautifulSoup
import time
import pandas as pd
import FinanceDataReader as fdr

 

이제 url을 requests.get에 전달하여 데이터를 가져온다. 

여기서 base_url은 검색한 결과의 주소창에서 seName=000660으로 되어 있는 것을 보고, 저렇게 설정한 것이다. 

base_url을 설정하는 것은 사실 여러 개를 검색해보고 어느 부분이 바뀌는지만 확인하면 되는 작업이라 어렵지 않다.  

code = "000660" # SK 하이닉스
base_url = "https://search.itooza.com/search.htm?seName={}&jl=&search_ck=&sm=&sd=2021-08-09&ed=2021-09-08&ds_de=&page=&cpv="
url = base_url.format(code)
html = requests.get(url, headers={'User-Agent':'Gils'}).content
soup = BeautifulSoup(html, 'lxml')

 

그 다음으로 크롬의 검사 기능을 이용하여 테이블이 페이지 소스 내에 어디있는지 확인한다.

크롬에서 테이블을 우클릭하여 검사를 누르면 아래와 같은 화면을 볼 수 있고, 보다시피 class="ex"인 table 요소로 정의된 것을 알 수 있다.

 

그러면 저 조건에 맞는 테이블을 find 메서드를 이용하여 찾아보자.

잘 찾은 것을 확인할 수 있고, 테이블이 thead와 tbody로 구분된 것을 볼 수 있다. 

페이지 소스에서는 아래와 같이 구현되어 있다.

테이블 헤더는 우리가 직접 정의하도록 하고, 바디만 살펴보자.

tr이라는 요소에 제목 셀이 th (예: 주당순이익 (EPS,연결지배))로 되어 있고, 값이 td로 되어 있는 것을 알 수 있다.

이제 테이블에서 body만 찾아 table_body에 저장하고, table_body에서 모든 행을 저장하도록 하자.

table_body = table.find('tbody')  # table에서 tbody만 가져옴
tr_list = table_body.find_all('tr') # tbody에서 행을 다 찾아서 저장

그리고나서 tr_list에 있는 모든 tr에 대해 th를 key로 td로 구성된 리스트를 value로 하는 사전인 result_dict을 만들어서 저장하자.

이 사전은 pd.DataFrame.to_dict()를 이용하여 데이터프레임으로 변환될 것이다.

result_dict = dict()
for tr in tr_list:
    col_name = tr.find('th').text # 투자 지표 이름
    values = [td.text for td in tr.find_all('td')] # tr에 있는 모든 td의 text만 가져와서 저장
    result_dict[col_name] = values # 사전으로 저장

마지막으로 result_dict을 데이터프레임으로 변환하자.

단, 분기정보는 손실되었으므로 (thead에 있었다!), 직접 분기 컬럼을 만들어서 저장하자.

Q = ["2021-2Q", "2021-1Q",
       "2020-4Q", "2020-3Q", "2020-2Q", "2020-1Q",
       "2019-4Q", "2019-3Q", "2019-2Q", "2019-1Q",
       "2018-4Q", "2018-3Q"] # 인덱스를 직접 정의


result = pd.DataFrame.from_dict(result_dict)
result.drop('주가', axis = 1, inplace = True) # 주가열도 필요없으니 삭제
result['분기'] = Q
result.head()

이 과정을 모두 거치면 아래와 같은 데이터프레임이 생성된다.

 

전 종목 수집

이제 전 종목의 투자 지표를 수집해보자.

먼저 전 종목의 코드와 이름이 필요하므로, fdr 모듈을 사용하여 목록을 가져온다.

stocks = fdr.StockListing('KRX') # 코스피, 코스닥, 코넥스 전체
stocks.dropna(inplace = True) # 지표가 없는 선물 등은 홈페이지, 지역, 산업 등이 모두 결측으로 되어 있으므로 삭제하면 됨
base_url = "https://search.itooza.com/search.htm?seName={}&jl=&search_ck=&sm=&sd=2021-08-09&ed=2021-09-08&ds_de=&page=&cpv="

두번째 라인에서 결측을 삭제한 이유는 투자지표가 없는 채권이나 선물 등은 stocks 데이터에서 홈페이지, 지역, 산업 등이 모두 결측으로 되어 있기 때문이다.

 

그러면 위에서 소개한 내용들을 모두 합쳐서, 아래와 같은 메인 코드를 작성하자.

이 코드는 stocks에서 symbol과 name을 순회하면서 url을 가져오고, 위에서 봤던대로 크롤링을 한다.

몇 가지 차이가 있다면, url에서 데이터를 가져올 때 오류가 뜨면 15분 재우는 것을 while 문으로 구현한 것과 ex라는 table이 존재하지 않으면 다음 종목으로 넘어가도록 구현하였다.

그리고 수집한 데이터는 종목명으로 저장한다.

for code, name in stocks[['Symbol', 'Name']].values:
    print(name)
    while True:
        try:
            url = base_url.format(code)
            html = requests.get(url, headers={'User-Agent':'Gils'}).content
            soup = BeautifulSoup(html, 'lxml')
            break
        except:
            time.sleep(15 * 60)
    
    # 만약 해당 종목 자료가 없다면, 아래 테이블이 없음
    table = soup.find("table", {"class":"ex"}) # class가 ex인 테이블을 찾음
    
    if table == None: # class가 ex인 테이블이 없으면 크롤링하지 않음
        continue

    idx = ["2021-2Q", "2021-1Q",
           "2020-4Q", "2020-3Q", "2020-2Q", "2020-1Q",
           "2019-4Q", "2019-3Q", "2019-2Q", "2019-1Q",
           "2018-4Q", "2018-3Q"] # 인덱스를 직접 정의

    table_body = table.find('tbody')  # table에서 tbody만 가져옴
    tr_list = table_body.find_all('tr') # tbody에서 행을 다 찾아서 저장

    result_dict = dict()
    for tr in tr_list:
        col_name = tr.find('th').text # 투자 지표 이름
        values = [td.text for td in tr.find_all('td')] # tr에 있는 모든 td의 text만 가져와서 저장
        result_dict[col_name] = values # 사전으로 저장

    result = pd.DataFrame.from_dict(result_dict)
    result.drop('주가', axis = 1, inplace = True) # 주가열도 필요없으니 삭제
    result['분기'] = Q
    
    result.to_csv(name + ".csv", index = False, encoding = "cp949")
    
    time.sleep(1) # 1초씩 재우기

 

전체 소스코드는 아래에서 받을 수 있다.

투자지표수집.ipynb
0.02MB

 

수집하고 싶은 금융 데이터나 실험하고 싶은 퀀트 관련 아이디어가 있으면 댓글로 남겨주세요! 
관련 포스팅을 준비하도록 하겠습니다!
Comments