본문 바로가기

프로그래밍/DART 재무제표 크롤링

DART 재무제표 크롤링 - 엑셀에 데이터 정리

DART API를 이용하여 받은 재무제표 데이터를 엑셀로 정리하는 코딩을 하였다. 파이썬 - xlwings 모듈을 활용하였다.

반대로 재무제표를 불러올 종목코드는 엑셀에서 불러오도록 하였다.

 

이전 프로그램에서 추가로 영업활동으로 인한 현금흐름과 주당이익을 추가하기 위하여, 전체 재무제표 정보를 활용하였다.

 

opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS003&apiId=2019020

 

전자공시 OPENDART 시스템 | 개발가이드 | 상세

상장기업 재무정보 개발가이드 상장기업 재무정보 단일회사 전체 재무제표 단일회사 전체 재무제표 개발가이드 기본 정보 기본 정보 메서드 요청URL 인코딩 출력포멧 GET https://opendart.fss.or.kr/api/

opendart.fss.or.kr

 

1. 전체 재무제표는 개별/연결 구분 정보를 추가로 요구한다. 연결재무제표(CFS)를 우선으로 확인하고, 없는 경우엔 재무제표(OFS) 를 확인하도록 하였다.

 

2. 힘들었던 부분은 재무제표마다 재무 항목을 나타내는 양식이 일치하지 않았다. 예를들어 매출액을 나타내는 단어는

'수익(매출액)', '영업수익', '매출액 및 지분법손익', '매출', 'I. 매출액','I.매출액','I.매출', '수익', '영업수익(매출액)' 등이 있었다. 각 재무 항목을 표현하는 단어들의 리스트를 작성하여, 이 리스트 항목이 재무제표에 있는 경우 값을 읽오도록 하였다.

 

3. 주당이익엔 계속주당이익과 중단주당이익이 있다. 주당이익은 두 가지 항목의 합으로 반영될 수 있도록 하였다.

 

 

전체코드

import xlwings as xw
from bs4 import BeautifulSoup
from urllib.request import urlopen
import pandas as pd
import xml.etree.ElementTree as elemTree
import time

#기업 코드 불러오기
tree = elemTree.parse("corp_num\CORPCODE.xml")
datalist = tree.findall("list")
corp_code = [x.findtext("corp_code") for x in datalist]
corp_name = [x.findtext("corp_name") for x in datalist]
stock_code = [x.findtext("stock_code") for x in datalist]

stocklist={}

for i in range(len(corp_code)):
    stocklist[stock_code[i]]=[corp_name[i], corp_code[i]]

#API 키
auth_key = '##########################'

#데이터 불러오기 함수
def get_Data(stock_code, start_year):

    stock_code_find = str(stock_code)
    #종목 코드를 이용하여 기업 코드 찾기
    corp_code_find = stocklist[stock_code_find][1]

    #보고서를 읽어오고자 하는 해당 년도
    start_year=str(start_year)

    report_list = ['11011','11014','11012','11013']
    # 1분기 보고서 : 11013
    # 반기 보고서 : 11012
    # 3분기 보고서 : 11014
    # 사업 보고서 : 11011

    # 분기 별로 1분기, 반기, 3분기, 사업 보고서를 차례로 불러옴
    for report_temp in report_list:
        report_code = report_temp

        #공시 시기 리스트 작성
        report_type = {'11013':'03','11012':'06','11014':'09','11011':'12'}
        report_date = start_year+"_"+report_type[report_code]

        #xml 형식으로 데이터 요청
        url = 'https://opendart.fss.or.kr/api/fnlttSinglAcntAll.xml?crtfc_key=' + auth_key + '&corp_code=' + corp_code_find + '&bsns_year=' + start_year + '&reprt_code=' + report_code + '&fs_div=CFS'

        data = urlopen(url)
        data_xml = BeautifulSoup(data.read(),'html.parser')

        #재무제표 정보가 포함된 list 항목만 추출
        data_list = data_xml.find_all("list")

        #연결재무제표가 없는 경우
        if len(data_list) == 0:
            url = 'https://opendart.fss.or.kr/api/fnlttSinglAcntAll.xml?crtfc_key=' + auth_key + '&corp_code=' + corp_code_find + '&bsns_year=' + start_year + '&reprt_code=' + report_code + '&fs_div=OFS'

            data = urlopen(url)
            data_xml = BeautifulSoup(data.read(), 'html.parser')

        data_list = data_xml.find_all("list")


        #print(data_list)


        value =[]
        index =[]
        data_dic={}

        #thstrm_amount : 당기의 재무 정보
        #sj_nm : 연결 재무제표 여부
        #account_nm : 재무제표 항목
        #연결 재무 여부와 항목을 합쳐서 index로 사용
        for i in data_list:
            if i.sj_nm.string != "자본변동표":
                value.append(i.thstrm_amount.string)
                index.append(i.account_nm.string)
                data_dic[i.account_nm.string]=i.thstrm_amount.string
        print(data_dic)
        print(1)


        #전체 data 중 관심있는 항목만 추출
        output_data={df1.index[0]:0, df1.index[1]:0, df1.index[2]:0, df1.index[3]:0, df1.index[4]:0, df1.index[5]:0, df1.index[6]:0, df1.index[7]:0, df1.index[8]:0}
        if len(data_dic) != 0:
            for i in df1.index:
                if i in data_dic:
                    output_data[i]=data_dic[i]
                    print(i)
                    print('OK')
                elif i == '분기순이익':
                    print(i)
                    print('NOK')
                    nm_list = ['당기순이익(손실)','1. 당기순이익(손실)', '당기순이익', '당기순손익', '연결당기순이익', '반기순이익(손실)', '반기순이익', 'VI.당기순손익', '당(반)기순이익',
                               '연결분기(당기)순이익', '연결반기순이익', '분(반)기순이익(손실)','순이익(손실)', '분기순이익(손실)', '분기순손익', '연결분기순이익']

                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]

                elif i == '자본총계':
                    print(i)
                    print('NOK')
                    nm_list = ['자본 총계']

                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]

                elif i == '자산총계':
                    print(i)
                    print('NOK')
                    nm_list = ['자산 총계']

                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]

                elif i == '부채총계':
                    print(i)
                    print('NOK')
                    nm_list = ['부채 총계']

                    for j in nm_list:

                        if j in data_dic:
                            output_data[i] = data_dic[j]

                elif i == '영업이익':
                    print(i)
                    print('NOK')
                    nm_list = ['IV. 영업이익(손실)', '영업이익(손실)','IV.영업이익(손실)', 'IV.영업이익', '영업손실', '영업이익 (손실)']

                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]

                elif i == '매출액':
                    print(i)
                    print('NOK')
                    nm_list = ['수익(매출액)', '영업수익', '매출액 및 지분법손익', '매출', 'I. 매출액','I.매출액','I.매출', '수익', '영업수익(매출액)']


                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]


                elif i == '영업활동현금흐름':
                    print(i)
                    print('NOK')
                    nm_list = ['영업활동으로 인한 현금흐름', 'I.영업활동으로 인한 현금흐름', 'I. 영업활동으로 인한 현금흐름', '영업활동으로 인한 순현금흐름', 'I. 영업활동현금흐름', 'I.영업활동현금흐름', '영업활동 현금흐름',
                               '영업활동으로인한현금흐름', 'I. 영업활동 현금흐름']

                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]

                elif i == '유동자산':
                    print(i)
                    print('NOK')
                    nm_list = ['I.유동자산']

                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]

                elif i == '기본주당이익':
                    print(i)
                    print('NOK')
                    nm_list = ['지배기업의 소유주에 대한 기본및희석주당이익', '지배기업의 소유주에 대한 기본 및 희석 주당이익', '지배기업의 소유주지분에 대한 기본및희석주당이익',
                               '기본주당이익(손실)', '기본및희석주당이익(손실)', '기본 및 희석주당이익(손실)', '기본 주당이익', '기본 주당이익(손실)', '기본주당순이익', '기본 주당 순이익',
                               '지배기업의 지분에 대한 주당이익(손실)', '보통주 기본 및 희석주당순손익','보통주 기본및희석주당이익(손실)','지배기업소유주 희석 주당이익', '기본 및 희석주당이익',
                               '기본및희석주당손익', '기본주당손익', '기본희석주당손익', '기본 및 희석 보통주당이익',
                               '기본주당이익(손실) (단위:원)', '기본주당순이익(손실)', '기본주당분기순이익', '기본주당반기순이익', '주당순이익', '기본및희석주당순이익', '기본 및 희석 주당이익(손실)',
                               '보통주 기본주당이익', '보통주 기본주당이익(손실)', '보통주주당순손실', '보통주주당순이익(손실)',
                               '보통주 주당순이익', '보통주 주당순이익(손실)', '기본 및 희석 주당 이익', '지배기업지분에 대한 주당순이익', '보통주 기본주당중단영업이익', '보통주 기본 및 희석주당이익 (단위: 원)',
                               '보통주 기본주당손익', '보통주 기본및희석주당손익','기본 및 희석주당순이익(손실)','중단영업 기본주당순손익', '보통주 기본주당순이익','보통주기본주당중단영업이익(손실)']

                    nm_list_con = ['계속영업주당이익(손실)', '계속영업기본주당이익(손실)', '계속영업주당이익','계속영업주당이익(손실)','계속영업주당이익 (손실)', '계속영업기본주당순이익',
                                '계속및희석주당이익', '계속영업희석주당이익(손실)', '계속영업주당손익', '계속기본주당이익(손실)']

                    nm_list_stop = ['중단영업주당이익(손실)', '중단영업기본주당이익(손실)', '중단영업주당이익', '중단영업주당이익(손실)', '중단영업주당이익 (손실)','중단영업기본주당순이익',
                                '중단및희석주당이익', '중단영업희석주당이익(손실)', '중단영업주당손익', '중단기본주당이익(손실)']

                    a = 0
                    b = 0
                    
                    #주당이익이 있으로 표현된 경우
                    for j in nm_list:
                        if j in data_dic:
                            output_data[i] = data_dic[j]
                    
                    
                    #계속 / 중단 주당이익으로 나뉘어 표현된 경우
                    for j in nm_list_con:
                        if j in data_dic:
                            EPS_con = float(data_dic[j])
                            print(type(EPS_con))
                            a=1

                    for j in nm_list_stop:
                        if j in data_dic:
                            EPS_stop=data_dic[j]
                            if str(type(EPS_stop)) != "<class 'NoneType'>":
                                EPS_stop = float(data_dic[j])
                            else:
                                EPS_stop = 0
                            b=1

                    if a+b == 2:
                        output_data[i] = EPS_con + EPS_stop

        else:
            for i in df1.index:
                output_data[i]=0

        print(output_data)

        #데이터 프레임에 열 추가
        df1[report_date]=output_data.values()


#엑셀시트 지정
wb = xw.Book('korea_stock_dart.xlsm')
sheet = wb.sheets[0]

stock_code_row=8
stock_code_column=2

while stock_code_row < 104:

    # 데이터 프레임 index, 얻고자 하는 항목
    data_index = {'유동자산', '자산총계', '자본총계', '부채총계', '매출액', '영업이익', '분기순이익', '영업활동현금흐름', '기본주당이익'}
    # 초기값
    init_value = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    df1 = pd.DataFrame(data=init_value, index=data_index)





    stock_code=sheet.range(stock_code_row,stock_code_column).value
    print(stock_code)

    #2017~2020 정보요청
    get_Data(stock_code,2020)
    get_Data(stock_code,2019)
    get_Data(stock_code,2018)
    get_Data(stock_code,2017)

    #초기값 삭제
    del df1[0]

    print(1)
    print(df1)

    row=1
    column=67

    #데이터 엑셀 위치 고정
    data_position= {'유동자산':7, '자산총계':4, '자본총계':5, '부채총계':6, '매출액':3, '영업이익':2, '분기순이익':0, '영업활동현금흐름':1, '기본주당이익':8}

    for i in df1.index:

        column_temp = column + data_position[i] * 18

        j=0
        while j < len(df1.loc[i]):
            sheet.range(stock_code_row, column_temp -1).value = i
            sheet.range(stock_code_row,column_temp+j).value=df1.loc[i][j]
            j=j+1


    stock_code_row = stock_code_row +1

 

 

결과

 

세로 항목은 종목코드이다. (엑셀에 종목코드만 입력하면 현재 가격, 시총 등 기본 정보는 제공해준다.)

매출액을 최신 부터 오래된 데이터 순서로 정리하여 변화 추이를 확일 할 수 있도록 하였다.

 

'프로그래밍 > DART 재무제표 크롤링' 카테고리의 다른 글

DART 재무제표 크롤링  (0) 2020.12.27
DART 기업 코드 크롤링  (0) 2020.12.27