본문 바로가기

파이썬으로 퀀트 프로그램 만들기 project/웹 크롤링

국내 주식 재무제표 크롤링하기_1

728x90

네이버 증권 사이트에서도 재무제표 정보를 제공하기는 하지만 동적 페이지라서 셀레니움을 사용해야 합니다.

셀레니움의 단점은 느리다는 것이 단점입니다.

네이버 증권 사이트 대신에 CompanyGuide 사이트를 이용하겠습니다. 여기서 재무제표를 클릭해주세요.

그리고 사실 해당 페이지 url 주소에서 ~gicode=A005930 뒤에 부분은 필요없는 부분 입니다.

그리고 gicode 뒤에가 티커 정보임을 확인할 수 있습니다.

또한 재무제표 내용이 테이블 형태로 나오므로 pandas를 이용하기 좋습니다.

 

 

#파이썬 실습

우선 최근일에 해당하는 티커정보를 불러옵니다. 지난번에 sql에 저장해두었던 데이터를 이용합니다.

지난번 블로그를 참조해주세요.

from sqlalchemy import create_engine
import pandas as pd

#최근일에 해당하는 티커정보 불러오기
engine = create_engine('mysql+pymysql://root:8019@127.0.0.1:3306/stock_db')
query = """
select * from kor_ticker
where 기준일 = (select max(기준일) from kor_ticker)
and 종목구분 = '보통주';
"""

ticker_list = pd.read_sql(query, con=engine)
engine.dispose()

 

첫번째 티커에 해당하는 재무제표 데이터를 크롤링 합니다.

i = 0
ticker = ticker_list['종목코드'][i]

url = f'https://comp.fnguide.com/SVO2/ASP/SVD_Finance.asp?pGB=1&gicode=A{ticker}'

data = pd.read_html(url)

 

이제 크롤링된 data를 살펴봅니다. data를 보면 사이트에서 제공되는 정보가 덜 담긴 듯 합니다. 그 이유는 사이트를 보시면 플러스 버튼으로 데이터가 숨겨져 있는 것을 확인할 수 있습니다. 그냥 read_html() 하면 화면에 보이는 부분만 크롤링 됩니다. 그러므로 read_html(url, displayed_only = False)해야 보이지 않는 데이터도 모두 가져올 수 있습니다.

i = 0
ticker = ticker_list['종목코드'][i]

url = f'https://comp.fnguide.com/SVO2/ASP/SVD_Finance.asp?pGB=1&gicode=A{ticker}'

data = pd.read_html(url, displayed_only=False)

 

data 변수에 담긴 정보들을 확인해 봅시면 다음을 알 수 있습니다.

data_0 = data[0] # 연간 손익계산서
data_1 = data[1] # 분기 손익 계산서
data_2 = data[2] # 연간 재무상태표
data_3 = data[3] # 분기 재무상태표
data_4 = data[4] # 연간 현금흐름표
data_5 = data[5] # 분기 현금 흐름표

 

 

#데이터 클랜징

손익 계산서 테이블에 열을 살펴보면 '전년동기', '전년동기(%)' 가 있습니다. 저희에게는 필요없는 열이므로 삭제해주도록 하겠습니다. 그리고 연간 데이터 끼리 합치겠습니다.

data_fs_y = pd.concat([
    data[0].iloc[:, ~data[0].columns.str.contains('전년동기')],
    data[2],
    data[4]
])

 

또한, 연결재무제표의 첫번째 열이름이 'IFRS(연결)'로 나오지만 별도재무제표는 다른 이름으로 나옵니다.

해당 열의 이름을 통일해줄 필요가 있습니다.

data_fs_y = data_fs_y.rename(columns={data_fs_y.columns[0]: "계정"})

 

data_fs_y에 담긴 테이블을 보시면 연간 데이터이지만 마지막에 분기 데이터가 있습니다.

결산마감 이전에 크롤링할 경우 분기 데이터가 포함될 때도 있습니다.

그래서 연간재무제표 데이터만 크롤링하기 위해서는 해당 종목의 결산월을 알아야 합니다.

결산월에 해당하는 정보는 아래 이미지에 빨간줄 위치에 제공됩니다.

 

 

해당 위치를 크롤링하기 위해 개발자도구를 열어서 검사를 해보면 corp_group1클래스의 div 태그 -> h2태그에 해당 부분이 있음을 알 수 있습니다. 그리고 정규표현식을 사용하면 숫자에 해당하는 부분을 얻어낼 수 있습니다.

import requests as rq
from bs4 import BeautifulSoup
import re

page_data = rq.get(url)
page_data_html = BeautifulSoup(page_data.content)

fiscal_data = page_data_html.select('div.corp_group1 > h2')

fiscal_data_text = fiscal_data[1].text
fiscal_data_text = re.findall('[0-9]+', fiscal_data_text)

 

이제 이렇게 뽑아낸 숫자를 이용해봅시다.

연간 데이터에서 분기 데이터를 제외하기 위해서는 열 이름의 끝이 결산월과 동일한 열만 선택하면 됩니다.

data_fs_y = data_fs_y.loc[:, (data_fs_y.columns == '계정') |
                            data_fs_y.columns.str[-2:].isin(fiscal_data_text) ]

 

데이터 클랜징할 것을 더 살펴보면 연간데이터의 행 이름들을 살펴보면 여러 개인 것들이 있습니다.

 

여기서 보시면 기타, 배당금수익, 파생상품이익등 대부분 중요하지 않은 것들이라서 1개만 남기도록 합시다.

def clean_fs(df, ticker, frequency):

    df = df[~df.loc[:, ~df.columns.isin(['계정'])].isna().all(axis=1)]
    df = df.drop_duplicates(['계정'], keep='first')
    df = pd.melt(df, id_vars ='계정', var_name='기준일', value_name='값')
    df = df[~pd.isnull(df['값'])]
    df['계정'] = df['계정'].replace({'계산에 참여한 계정 펼치기': ''}, regex=True)
    df['기준일'] = pd.to_datetime(df['기준일'],
                               format="%Y/%m") + pd.tseries.offsets.MonthEnd()
    df['종목코드'] = ticker
    df['공시구분'] = frequency
    
    return df
    

data_fs_y_clean = clean_fs(data_fs_y, ticker, 'y')

 

분기 재무제표 데이터도 클랜징 해봅시다.

#분기 데이터 합치기
data_fs_q = pd.concat(
    [data[1].iloc[:, ~data[1].columns.str.contains('전년동기')], data[3], data[5]])
data_fs_q = data_fs_q.rename(columns={data_fs_q.columns[0]: "계정"})
data_fs_q_clean = clean_fs(data_fs_q, ticker, 'q')

 

마지막으로 연간 재무제표, 분기 재무제표 데이터를 모두 합칩니다.

#연간, 분기 재무제표 합치기
data_fs_bind = pd.concat([data_fs_y_clean, data_fs_q_clean])

 

다음 시간에는 모든 종목의 재무제표를 크롤링해보겠습니다.