반응형

[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:]


| PLUS!


아래와 같이 DataFrame의 read_html 기능을 이용해서 아주 쉽게 재무제표 데이터를 구해올 수도 있습니다. 위의 내용은 크롤링의 전반적인 내용을 담고 있기 때문에 읽어 두시고 이렇게 쉬운 방법으로 데이터를 구할 수 있다는 것도 참고하세요~

import pandas as pd
import requests

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

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

financial_stmt = pd.read_html(samsung_electronic.text)[3]

financial_stmt.set_index(('주요재무정보', '주요재무정보', '주요재무정보'), inplace=True)
financial_stmt.index.rename('주요재무정보', inplace=True)
financial_stmt.columns = financial_stmt.columns.droplevel(2)
print(financial_stmt)
             최근 연간 실적                          ...   최근 분기 실적                      
2017.12 2018.12 2019.12 ... 2019.09 2019.12 2020.03(E)
주요재무정보 ...
매출액 2395754.00 2437714.00 2304009.00 ... 620035.00 598848.00 557762.00
영업이익 536450.00 588867.00 277685.00 ... 77779.00 71603.00 63238.00
당기순이익 421867.00 443449.00 217389.00 ... 62877.00 52270.00 47614.00
영업이익률 22.39 24.16 12.05 ... 12.54 11.96 11.34
순이익률 17.61 18.19 9.44 ... 10.14 8.73 8.54
ROE(지배주주) 21.01 19.63 8.69 ... 10.05 8.69 NaN
부채비율 40.68 36.97 34.12 ... 34.14 34.12 NaN
당좌비율 181.61 204.12 233.57 ... 235.80 233.57 NaN
유보율 24536.12 27531.92 28856.02 ... 28541.64 28856.02 NaN
EPS(원) 5421.00 6024.00 3166.00 ... 899.00 770.00 760.00
PER(배) 9.40 6.42 17.63 ... 13.73 17.63 60.23
BPS(원) 30427.00 35342.00 37528.00 ... 37600.00 37528.00 NaN
PBR(배) 1.67 1.10 1.49 ... 1.30 1.49 NaN
주당배당금(원) 850.00 1416.00 1416.00 ... NaN NaN NaN
시가배당률(%) 1.67 3.66 2.54 ... NaN NaN NaN
배당성향(%) 14.09 21.92 44.73 ... NaN NaN NaN


반응형

이 글을 공유하기

댓글

Designed by JB FACTORY