본문 바로가기
파이썬/주식

Python, BeautifulSoup4으로 Naver Finance 정보 크롤링 네이버 기업실적 크롤링

by 행복론자 2019. 11. 24.

오늘은 BeautifulSoup4으로 finace.naver.com 에 있는 기업의 재무정보를 크롤링하는 법을 포스팅해보겠습니다.

 

준비물 : BeautifulSoup4, lxml

 

크롤링할 대상은 기업마다 존재하는 아래 기업실적분석 정보입니다. 

 

 

BeautifulSoup을 이용해서 위의 정보를 받아오려면 크롤링할 페이지의 URL그리고 긁어올 부분의 html tag를 알아야 합니다. 위 테이블의 tag를 보기 위해서는 크롬기준 F12를 눌러 해당 tag의 class를 확인합니다.

확인해보니 div tag의 class명은 cop_analysis입니다. 

이 정보를 바탕으로 크롤링 해보겠습니다. 

 

 

 

 

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

BASE_URL = 'https://finance.naver.com'

def crawl(code):
    req = requests.get(BASE_URL + '/item/main.nhn?code=' + code)
    page_soup = BeautifulSoup(req.text, 'lxml')
    finance_html = page_soup.select_one('div.cop_analysis')

req로 받아온 정보를 BeautifulSoup을 사용해 parsing했습니다. 그 중에서 div tag로 된 classname = 'cop_analysis'를 긁어옵니다. 

 

 

finace_html을 확인해보면 우리가 필요한 테이블 정보가 들어가 있습니다.

이 정보들을 이쁘게 꺼내보겠습니다.

 

따로 구분 지어야 할 정보는 크게 세가지입니다.

 

빨간색으로 표시한 부분 index로 사용할 부분들이고 파란색으로 표시한 날짜정보는 column으로 사용하겠습니다.

그리고 table 안에 있는 초록색 부분(td tag)는 이에 해당하는 정보들입니다.

이 들을 각각 구분 지어 보겠습니다. 

 

def crawl(code):
    req = requests.get(BASE_URL + '/item/main.nhn?code=' + code)   
    page_soup = BeautifulSoup(req.text, 'lxml')    
    finance_html = page_soup.select_one('div.cop_analysis')
    th_data = [item.get_text().strip() for item in finance_html.select('thead th')]

    annual_date = th_data[3:7] # ['2016.12', '2017.12', '2018.12', '2019.12(E)']
    quarter_date = th_data[7:13] # ['2018.09', '2018.12', '2019.03', '2019.06', '2019.09', '2019.12(E)']

첫번째로 날짜 정보를 뽑아내겠습니다. 이 정보들은 table의 thead 밑에 각각 th에 해당합니다. 

finance_html에서 thead 부분 중 th를 뽑아낸 th_data에는 날짜정보가 순서대로 들어가 있으나 이외에도 필요 없는 정보들이 들어가 있습니다. th_data 안에만 보면 이렇게 되어있습니다. 

 

['주요재무정보', '최근 연간 실적', '최근 분기 실적', '2016.12', '2017.12', '2018.12', '2019.12(E)', '201809', '2018.12', '2019.03', '2019.06', '2019.09', '2019.12(E)', 'IFRS연결', 'IFRS연결', 'IFRS연결', 'IFRS연결', 'IFRS연결', 'IFRS연결', 'IFRS연결', 'IFRS연결', 'IFRS연결', 'IFRS연결']

 

이중 필요한 정보들(annual_date, quarter_date)만 걸러내겠습니다. 

 

 

def crawl(code):
    req = requests.get(BASE_URL + '/item/main.nhn?code=' + code)
    page_soup = BeautifulSoup(req.text, 'lxml')
    finance_html = page_soup.select_one('div.cop_analysis')
    th_data = [item.get_text().strip() for item in finance_html.select('thead th')]
    
    annual_date = th_data[3:7] # ['2016.12', '2017.12', '2018.12', '2019.12(E)']
    quarter_date = th_data[7:13] # ['2018.09', '2018.12', '2019.03', '2019.06', '2019.09', '2019.12(E)']

    finance_index = [item.get_text().strip() for item in finance_html.select('th.h_th2')][3:] 
    # ['주요재무정보', '최근 연간 실적', '최근 분기 실적', '매출액', '영업이익', '당기순이익', '영업이익률', '순이익률', 'ROE(지배주주)', '부채비율', '당좌비율', '유보율', 'EPS(원)', 'PER(배)', 'BPS(원)', 'PBR(배)', '주당배당금(원)', '시가배당률(%)', '배당성향(%)']
    finance_data = [item.get_text().strip() for item in finance_html.select('td')]

 

 

다음은 각각이 숫자들이 무엇을 의미하는지 나타내는 index(ex 매출액, 영업이익)을 뽑아내겠습니다.

주요재무정보 항목들을 나타냅니다. 이 부분은 finance_html 중 classname='h_th2'인 th들입니다.

하지만 전부를 뽑아내보면 

 

['주요재무정보', '최근 연간 실적', '최근 분기 실적', '매출액', '영업이익', '당기순이익', '영업이익률', '순이익률', 'ROE(지배주주)', '부채비율', '당좌비율', '유보율', 'EPS(원)', 'PER(배)', 'BPS(원)', 'PBR(배)', '주당배당금(원)', '시가배당률(%)', '배당성향(%)']

 

이런 정보가 들어가 있습니다. 마찬가지로 앞에 필요없는 부분을 제외하고 index에 담겠습니다.

다음은 data라는 항목으로 table의 td(숫자)들을 finance_data에 담아넣겠습니다.

이렇게 넣은 finance_data는 일차원 형태이나 실제 테이블 구조처럼 N x N으로 바꿔줘야합니다.

이 작업까지 한 코드입니다.

 

def crawl(code):
    req = requests.get(BASE_URL + '/item/main.nhn?code=' + code)
    page_soup = BeautifulSoup(req.text, 'lxml')
    finance_html = page_soup.select_one('div.cop_analysis')
    th_data = [item.get_text().strip() for item in finance_html.select('thead th')]

    annual_date = th_data[3:7] # ['2016.12', '2017.12', '2018.12', '2019.12(E)']
    quarter_date = th_data[7:13] # ['2018.09', '2018.12', '2019.03', '2019.06', '2019.09', '2019.12(E)']

    finance_index = [item.get_text().strip() for item in finance_html.select('th.h_th2')][3:]  # ['주요재무정보', '최근 연간 실적', '최근 분기 실적', '매출액', '영업이익', '당기순이익', '영업이익률', '순이익률', 'ROE(지배주주)', '부채비율', '당좌비율', '유보율', 'EPS(원)', 'PER(배)', 'BPS(원)', 'PBR(배)', '주당배당금(원)', '시가배당률(%)', '배당성향(%)']
    finance_data = [item.get_text().strip() for item in finance_html.select('td')]

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

resize를 통해 (index의 개수) x (10, 연, 분기 날짜만큼)으로 나눴습니다.

이를 pandas DataFrame에 담아 넣고 연간, 분기로 나눠서 return하는 crawl함수를 완성합니다. 

 

이 부분을 모두 완성하여 "삼성전자=005930" 재무정보를 크롤링하는 전체 소스입니다. 

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

BASE_URL = 'https://finance.naver.com'

def crawl(code):
    req = requests.get(BASE_URL + '/item/main.nhn?code=' + code)
    page_soup = BeautifulSoup(req.text, 'lxml')
    finance_html = page_soup.select_one('div.cop_analysis')
    th_data = [item.get_text().strip() for item in finance_html.select('thead th')]

    annual_date = th_data[3:7] # ['2016.12', '2017.12', '2018.12', '2019.12(E)']
    quarter_date = th_data[7:13] # ['2018.09', '2018.12', '2019.03', '2019.06', '2019.09', '2019.12(E)']

    finance_index = [item.get_text().strip() for item in finance_html.select('th.h_th2')][3:]  # ['주요재무정보', '최근 연간 실적', '최근 분기 실적', '매출액', '영업이익', '당기순이익', '영업이익률', '순이익률', 'ROE(지배주주)', '부채비율', '당좌비율', '유보율', 'EPS(원)', 'PER(배)', 'BPS(원)', 'PBR(배)', '주당배당금(원)', '시가배당률(%)', '배당성향(%)']
    finance_data = [item.get_text().strip() for item in finance_html.select('td')]

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

    finance_date = annual_date + quarter_date
    finance = pd.DataFrame(data=finance_data[0:, 0:], index=finance_index, columns=finance_date)

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

    return finance,annual_finance,quarter_finance

finace,annual,quarter = crawl('005930')

print(quarter.iloc[5])

 

이름은 crawl이지만 가져와서 자르고 나누는 작업을 모두 다하는 함수를 완성했습니다.ㅎㅎ

전체 정보인 finace 및 연간, 분기 정보를 다 잘라서 반환하도록 했습니다.

 

여기서 분기의 5번째 행 (ROE)을 출력해보는 print(quarter.iloc[5])을 통해 잘 가져왔음을 알 수 있습니다.

원래 이 기업정보의 원천은 전자공시시스템(dart)을 통하므로 dart를 크롤링해서 사용하는 것이 더 많은 정보를 볼 수 있겠으나 이 포스팅에서는 간략한 정보를 이쁘게 담은 Naver finace를 크롤링해보았습니다. 

 

감사합니다. 

 

** 제 블로그에 와주셔서 감사합니다.
재밌게 보셨다면 좋아요, 댓글은 저에게 큰 힘이 됩니다! 
감사합니다!

 

반응형
이 포스팅은 쿠팡파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

댓글