[Python 재무제표 크롤링 #7] NAVER 금융에서 재무제표 데이터, 파이썬 데이터 프레임으로 추출하기

이전 포스팅과 이어지는 내용입니다.


▶[Python/Python 재무제표 크롤링] - [Python 재무제표 크롤링] NAVER 금융에서 재무제표 HTML 요소 추출하기


| 삼성전자 재무제표 부분 HTML 불러오기


삼성전자 재무제표 부분 HTML 를 불러오는 파이썬 스크립트입니다.

import requests
import numpy as np
import pandas as pd
from bs4 import BeautifulSoup


URL = "https://finance.naver.com/item/main.nhn?code=005930"

samsung_electronic = requests.get(URL)
html = samsung_electronic.text

soup = BeautifulSoup(html, 'html.parser')

finance_html = soup.select('div.section.cop_analysis div.sub_section')[0]


| 삼성전자 재무제표 RAW HTML 코드에서 데이터 추출하기


삼성전자 재무제표 RAW HTML 코드인 finance_html에서 재무제표의 각 연간 및 분기 날짜, 주요재무제표 정보인 매출액, 영업이익 ... 배당성향 등의 데이터를 추출해보도록 하겠습니다.


먼저 재무제표의 날짜 정보를 추출해 보도록 하겠습니다.



위 빨간색 안의 날짜정보들은 다음과 같이 thead 태그 및의 th 태그에 데이터가 위치해 있습니다.

<th class="" scope="col">
2015.12
</th>
<th class="" scope="col">
2016.12
</th>
<th class="" scope="col">
2017.12
</th>
<th class="t_line cell_strong" scope="col">
2018.12<em>(E)</em>
</th>
<th class="" scope="col">
2017.09
</th>
<th class="" scope="col">
2017.12
...


BeautifulSoup CSS Selector 방식으로 해당 날짜 데이터들을 파이썬의 리스트 형식으로 모두 추출할 수 있습니다. 

th_data = [item.get_text().strip() for item in finance_html.select('thead th')]
annual_date = th_data[3:7]
quarter_date = th_data[7:13]
['2015.12', '2016.12', '2017.12', '2018.12(E)']
['2017.09', '2017.12', '2018.03', '2018.06', '2018.09', '2018.12(E)']


주요재무정보의 각 명칭들을 추출해 보겠습니다.



매출액, 영업이익, 당기순이익 같은 재무정보 지표는 th 태그의 h_th2 클래스에 위치해 있습니다.

<th class="h_th2 th_cop_anal8" scope="row"><strong>매출액</strong></th>
...
<th class="h_th2 th_cop_anal11" scope="row"><strong>영업이익률</strong></th>
...

추출하는 방법은 다음과 같습니다.

finance_index = [item.get_text().strip() for item in finance_html.select('th.h_th2')][3:]
['매출액', '영업이익', '당기순이익', '영업이익률', '순이익률', 'ROE(지배주주)', '부채비율', '당좌비율', '유보율', 'EPS(원)', 'BPS(원)', '주당배당금(원)', '시가배당률(%)', '배당성향(%)']


마찬가지 방법으로 재무제표의 데이터들을 추출합니다. 



이 데이터들은 td 태그에 위치해있습니다. 

< td class ="" >


2,006,535

</td>
<td class ="" >


2,018,667

< td >

...

추출방법은 다음과 같습니다.

finance_data = [item.get_text().strip() for item in finance_html.select('td')]
['2,006,535', '2,018,667', '2,395,754', '2,508,293', '620,489', '659,784', '605,637', '584,827', '654,600', '663,198', '264,134', '292,407', '536,450', '642,924', '145,332', '151,470', '156,422', '148,690', '175,749', '162,062', '190,601', '227,261', '421,867', '484,835', '111,934', '122,551', '116,885', '110,434', '131,507', '125,966', '13.16', '14.49', '22.39', '25.63', '23.42', '22.96', '25.83', '25.42', '26.85', '24.44', '9.50', '11.26', '17.61', '19.33', '18.04', '18.57', '19.30', '18.88', '20.09', '18.99', '11.16', '12.48', '21.01', '21.14', '19.24', '21.01', '22.79', '21.77', '21.73', '', '35.25', '35.87', '40.68', '', '40.76', '40.68', '39.96', '36.70', '39.28', '', '209.74', '223.46', '181.61', '', '177.37', '181.61', '188.10', '197.58', '198.16', '', '21,117.88', '22,004.14', '24,536.12', '', '23,529.09', '24,536.12', '25,279.75', '26,235.70', '27,412.63', '', '2,198', '2,735', '5,421', '6,535', '1,487', '1,629', '1,583', '1,500', '1,771', '1,686', '23,715', '26,636', '30,427', '36,167', '29,716', '30,427', '31,782', '33,223', '34,519', '36,167', '420', '570', '850', '1,508', '', '', '', '', '', '', '1.67', '1.58', '1.67', '', '', '', '', '', '', '', '16.42', '17.81', '14.09', '', '', '', '', '', '', '']


| 넘파이(Numpy)로 데이터 변환하기


finance_data는 모든 재무제표 정보를 담고 있지만 하나의 리스트 형태로 되어있기 때문에 재무제표 데이터를 쉽게 분석하기 위해서는 전처리가 진행되어야 합니다.


파이썬 넘파이(Numpy)를 사용하여 하나의 리스트로 된 데이터를 위 재무제표정보와 같이 14 X 10 형태의 행렬로 변환해 보도록 하겠습니다.


import numpy as np

finance_data = np.array(finance_data)
finance_data.resize(len(finance_index), 10)

다음과 같이 데이터가 행렬형태로 변환됩니다

[['2,006,535' '2,018,667' '2,395,754' '2,508,293' '620,489' '659,784'
'605,637' '584,827' '654,600' '663,198']
['264,134' '292,407' '536,450' '642,924' '145,332' '151,470' '156,422'
'148,690' '175,749' '162,062']
['190,601' '227,261' '421,867' '484,835' '111,934' '122,551' '116,885'
'110,434' '131,507' '125,966']
['13.16' '14.49' '22.39' '25.63' '23.42' '22.96' '25.83' '25.42' '26.85'
'24.44']
['9.50' '11.26' '17.61' '19.33' '18.04' '18.57' '19.30' '18.88' '20.09'
'18.99']
['11.16' '12.48' '21.01' '21.14' '19.24' '21.01' '22.79' '21.77' '21.73'
'']
['35.25' '35.87' '40.68' '' '40.76' '40.68' '39.96' '36.70' '39.28' '']
['209.74' '223.46' '181.61' '' '177.37' '181.61' '188.10' '197.58'
'198.16' '']
['21,117.88' '22,004.14' '24,536.12' '' '23,529.09' '24,536.12'
'25,279.75' '26,235.70' '27,412.63' '']
['2,198' '2,735' '5,421' '6,535' '1,487' '1,629' '1,583' '1,500' '1,771'
'1,686']
['23,715' '26,636' '30,427' '36,167' '29,716' '30,427' '31,782' '33,223'
'34,519' '36,167']
['420' '570' '850' '1,508' '' '' '' '' '' '']
['1.67' '1.58' '1.67' '' '' '' '' '' '' '']
['16.42' '17.81' '14.09' '' '' '' '' '' '' '']]


finance_data와 위에서 구한 annual_date, quarter_date 그리고 finance_index를 합쳐 재무제표를 나타내는 데이터프레임을 만들어보도록 하겠습니다.


먼저 annual_date quarter_date를 합치고 난 후 전체 데이터를 데이터프레임으로 나타내는 작업을 할 수 있습니다.

finance_date = annual_date + quarter_date

import pandas as pd
finance = pd.DataFrame(data=finance_data[0:,0:], index=finance_index, columns=finance_date)
             2015.12    2016.12    ...        2018.09 2018.12(E)
매출액 2,006,535 2,018,667 ... 654,600 663,198
영업이익 264,134 292,407 ... 175,749 162,062
당기순이익 190,601 227,261 ... 131,507 125,966
영업이익률 13.16 14.49 ... 26.85 24.44
순이익률 9.50 11.26 ... 20.09 18.99
ROE(지배주주) 11.16 12.48 ... 21.73
부채비율 35.25 35.87 ... 39.28
당좌비율 209.74 223.46 ... 198.16
유보율 21,117.88 22,004.14 ... 27,412.63
EPS(원) 2,198 2,735 ... 1,771 1,686
BPS(원) 23,715 26,636 ... 34,519 36,167
주당배당금(원) 420 570 ...
시가배당률(%) 1.67 1.58 ...
배당성향(%) 16.42 17.81 ...

[14 rows x 10 columns]


만약 연간 재무제표와 분기 재무제표 둘로 나누고 싶다면 데이터프레임의 iloc메서드와 슬라이싱으로 간단하게 나눌수 있습니다.


annual_finance = finance.iloc[:, :4]
quarter_finance = finance.iloc[:, 4:]
            2015.12    2016.12    2017.12 2018.12(E)
매출액 2,006,535 2,018,667 2,395,754 2,508,293
영업이익 264,134 292,407 536,450 642,924
당기순이익 190,601 227,261 421,867 484,835
영업이익률 13.16 14.49 22.39 25.63
순이익률 9.50 11.26 17.61 19.33
ROE(지배주주) 11.16 12.48 21.01 21.14
부채비율 35.25 35.87 40.68
당좌비율 209.74 223.46 181.61
유보율 21,117.88 22,004.14 24,536.12
EPS(원) 2,198 2,735 5,421 6,535
BPS(원) 23,715 26,636 30,427 36,167
주당배당금(원) 420 570 850 1,508
시가배당률(%) 1.67 1.58 1.67
배당성향(%) 16.42 17.81 14.09
2017.09 2017.12 ... 2018.09 2018.12(E)
매출액 620,489 659,784 ... 654,600 663,198
영업이익 145,332 151,470 ... 175,749 162,062
당기순이익 111,934 122,551 ... 131,507 125,966
영업이익률 23.42 22.96 ... 26.85 24.44
순이익률 18.04 18.57 ... 20.09 18.99
ROE(지배주주) 19.24 21.01 ... 21.73
부채비율 40.76 40.68 ... 39.28
당좌비율 177.37 181.61 ... 198.16
유보율 23,529.09 24,536.12 ... 27,412.63
EPS(원) 1,487 1,629 ... 1,771 1,686
BPS(원) 29,716 30,427 ... 34,519 36,167
주당배당금(원) ...
시가배당률(%) ...
배당성향(%) ...

[14 rows x 6 columns]


| 전체 코드


import requests
from bs4 import BeautifulSoup

URL = "https://finance.naver.com/item/main.nhn?code=005930"

samsung_electronic = requests.get(URL)
html = samsung_electronic.text

soup = BeautifulSoup(html, 'html.parser')

finance_html = soup.select('div.section.cop_analysis div.sub_section')[0]

th_data = [item.get_text().strip() for item in finance_html.select('thead th')]
annual_date = th_data[3:7]
quarter_date = th_data[7:13]

finance_index = [item.get_text().strip() for item in finance_html.select('th.h_th2')][3:]

finance_data = [item.get_text().strip() for item in finance_html.select('td')]

import numpy as np

finance_data = np.array(finance_data)
finance_data.resize(len(finance_index), 10)

finance_date = annual_date + quarter_date

import pandas as pd
finance = pd.DataFrame(data=finance_data[0:,0:], index=finance_index, columns=finance_date)

annual_finance = finance.iloc[:, :4]
quarter_finance = finance.iloc[:, 4:]


이 글을 공유하기

댓글(2)

  • 김주녕
    2019.07.19 17:43

    finance_html = soup.select('div.section.cop_analysis div.sub_section')[0]

    th_data = [item.get_text().strip() for item in finance_html.select('thead th')]

    이 부분에서 finance_html 값이 list여서 그런지 pycharm에서 finance_html.select 구문이 작동하지 않더라구요.....

    • 2019.07.22 09:59 신고

      댓글 달아주셔서 감사합니다!
      이상하네요.. 지금 테스트 해 봤는데 정상작동하고 있습니다.

      혹시 소스 코드에 오타가 있는 지 확인부탁드려요 될까요?

Designed by JB FACTORY