개발이야기/Python

[python/scraping] Dart 전자공시 스크래핑

원효대사해골물 2020. 9. 27. 02:10
dart_scrolling_by_xpath

Selenium을 이용하여 Dart페이지에 접속하고 원하는 정보를 크롤링 하는 예시입니다.

Library import

In [1]:
from urllib.request import urlopen
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import pandas as pd
import time

Chrome start

selenium을 통한 크롬 제어는 아래 블로그가 너무 잘 정리되어 있습니다. 보고 오세요.

beomi.github.io/2017/02/27/HowToMakeWebCrawler-With-Selenium/

In [2]:
# chrome 시작
driver = webdriver.Chrome(r'C:\Users\dcsun\Downloads\000_Study\010_bbb_scrolling\chromedriver_win32\chromedriver.exe') # 윈도우
# driver = webdriver.PhantomJS('/usr/local/Cellar/phantomjs/2.1.1/bin/phantomjs') # (참고)맥

image.png

Dart 홈페이지 이동

In [3]:
# Dart 홈페이지 이동 
driver.get('http://dart.fss.or.kr')

image.png

원하는 기업 검색(by xpath)

앞으로 모든 과정이 xpath 주소를 찾아 이동하는 동일한 과정으로 반복하여 진행됩니다.(selector로도 가능함)

1. 크롬 화면에서 키보드 F12 클릭 하여 크롬 개발자 도구 창을 엽니다.

image.png

2. 검색창 Xpath 주소 찾기 - ①을 누르고 원하는 검색 창②을 누르면 해당 검색창 소스③를 표시해 줍니다.

image.png

3. 해당 소스를 우클릭하여 xpath 주소 복사

image.png

xpath를 활용하여 기업 검색

In [4]:
# 검색창 활성화
element = driver.find_element_by_xpath("//*[@id='textCrpNm']")

# 검색창 초기화
length = len(element.get_attribute('value'))
element.send_keys(length * Keys.BACKSPACE)

# 검색 기업 입력
element.send_keys("삼성전자")

image.png

원하는 공시 지정 - ex 정기공시 - 반기/분기보고서

위 검색창 xpath를 찾은 것과 동일하게 원하는 xpath를 찾아 진행하면 됨

In [5]:
# 원하는 공시 지정 - ex 정기공시 - 반기/분기보고서
## 정기공시 클릭
driver.find_element_by_xpath("//*[@id='publicTypeButton_01']").click()
time.sleep(1) # 클릭 후 반응을 고려하여 1초 쉬기
## 반기 보고서 클릭
driver.find_element_by_xpath("//*[@id='publicType2']").click ()
time.sleep(1) # 클릭 후 반응을 고려하여 1초 쉬기
## 분기 보고서 클릭
driver.find_element_by_xpath("//*[@id='publicType3']").click ()

image.png

기간 설정

In [6]:
## start day
# start day text input 창 선택
element = driver.find_element_by_xpath("//*[@id='startDate']")
# start day text input 창 선택 초기화
length = len(element.get_attribute('value'))
element.send_keys(length * Keys.BACKSPACE)
# start day text input 값 insert
element.send_keys("20190925")

## end day
# end day text input 창 선택
element = driver.find_element_by_xpath("//*[@id='endDate']")
# end day text input 창 선택 초기화
length = len(element.get_attribute('value'))
element.send_keys(length * Keys.BACKSPACE)
# end day text input 값 insert
element.send_keys("20200925")

image.png

검색 버튼 클릭 / 반기보고서 클릭

In [7]:
# 검색
driver.find_element_by_xpath("//*[@id='searchForm']/fieldset/p[4]/input").click ()

image.png

반기보고서에서 공시 내용을 dataframe으로 가지고 오기

(이부분은 main page에서 반기보고서/분기보고서를 찾아서 자동으로 가져오게 하는게 좋겠다. - 차후 추가 예정)

반기보고서 xpath를 통해 url 가지고 오기

In [8]:
# 반기보고서 xpath를 통해 반기보고서 url 가지고 오기
elm = driver.find_element_by_xpath("//*[@id='r_20200814001766']")
href = elm.get_attribute('href')
print(href) # debug
http://dart.fss.or.kr/dsaf001/main.do?rcpNo=20200814001766

반기보고서에서 요약재무정보 가지고 오기

이를 위해서는
(참고)http://dart.fss.or.kr/report/viewer.do?rcpNo=20200814001766&dcmNo=7446167&eleId=12&offset=573782&length=21486&dtd=dart3.xsd
반기보고서 main page의 html을 뒤져서 위와 같이 만들어야 한다.
아래 블로그를 참고하여 만들었다.

https://blog.naver.com/PostView.nhn?blogId=dreaming2b&logNo=221467450927&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

In [9]:
# input url 
html = urlopen(href) # 반기보고서 url

# 반기보고서 url로 반기보고서 html 호출
bsobjt = BeautifulSoup(html,"html.parser") 

# main url을 통해 html 호출하여 파싱 
body = str(bsobjt.find('head'))
body = body.split('요약재무정보",')[1] # input2
body = body.split('cnt++')[0]
body = body.split('viewDoc(')[1]
body = body.split(')')[0]
body = body.split(', ')
body = [body[i][1:-1] for i in range(len(body))]

# 요약재무정보 url 생성
url_final = 'http://dart.fss.or.kr/report/viewer.do?rcpNo=' + body[0] + '&dcmNo=' + body[1] + '&eleId=' + body[2] + '&offset=' + body[3] +'&length=' + body[4] + '&dtd=dart3.xsd'

# 요약재무정보 html 가지고 오기
html_final = urlopen(url_final)
bsobjt_final = BeautifulSoup(html_final,"html.parser")


# html 파싱을 통해 표 생성
ls_bf = bsobjt_final.findAll('tr')
_df_rlt = pd.DataFrame(columns = ['gubun','year_1','year_2','year_3'])
for i in ls_bf:
    _ls = []
    for j in i.findAll('td'):
        str_j = str(j)
        str_j = str_j.replace('[', '')
        str_j = str_j.replace(']', '')
        str_j = str_j.replace('\xa0ㆍ', '')
        if str_j.split('<')==3:
            _ls = _ls + ''
        else:
            _ls = _ls + [str_j.split('<')[1].split('>')[1]]
    if len(_ls) !=4:
        pass
    else :
        _df_rlt = _df_rlt.append(pd.DataFrame(data = [_ls] , columns = ['gubun','year_1','year_2','year_3']))
_df_rlt.reset_index(drop=True,inplace=True)

결과

In [10]:
_df_rlt.head(10)
Out[10]:
gubun year_1 year_2 year_3
0 2020년 6월말 2019년 12월말 2018년 12월말
1 유동자산 186,136,845 181,385,260 174,697,424
2 현금및현금성자산 36,109,613 26,885,999 30,340,505
3 단기금융상품 75,127,611 76,252,052 65,893,797
4 기타유동금융자산 1,807,206 5,641,652 4,705,641
5 매출채권 32,989,762 35,131,343 33,867,733
6 재고자산 29,645,529 26,766,464 28,984,704
7 기타 10,457,124 10,707,750 10,905,044
8 비유동자산 171,822,654 171,179,237 164,659,820
9 기타비유동금융자산 10,640,184 9,969,716 8,315,087