파이썬으로 DART에서 재무제표 수집하기

2021-03-28 • quantpython, dart, 재무제표 • 3 min read

이번 포스트에서는 전자공시시스템(DART)에서 제공하는 재무제표를 파이썬으로 다운로드하고 파싱하는 방법을 다룹니다.

DART에서 재무제표를 얻어오는 방법은 다양한데 여기서는 두 가지 방법을 다룹니다.

DART Open API 상장기업 재무정보

첫 번째로 단일회사 주요계정 API를 활용한 방법입니다.

DART Open API를 사용하려면 인증키가 필요합니다. 인증키 획득 방법은 이 포스트에서 확인하세요.

다음은 단일회사 주요계정 API를 호출하여 재무제표 정보를 획득하는 함수 입니다. 인증키, 회사 고유번호, 사업연도, 보고서 코드를 입력하여 재무제표를 받아올 수 있습니다.

def get_fs(crtfc_key, corp_code, bsns_year, reprt_code):
    # 단일회사 주요계정
    # crtfc_key API 인증키 STRING(40)  Y   발급받은 인증키(40자리)
    # corp_code 고유번호    STRING(8)   Y   공시대상회사의 고유번호(8자리)
    # ※ 개발가이드 > 공시정보 > 고유번호 API조회 가능
    # bsns_year 사업연도    STRING(1)   Y   사업연도(4자리)
    # ※ 2015년 이후 부터 정보제공
    # reprt_code    보고서 코드  STRING(5)   Y   1분기보고서 : 11013
    # 반기보고서 : 11012
    # 3분기보고서 : 11014
    # 사업보고서 : 11011
    api = 'https://opendart.fss.or.kr/api/fnlttSinglAcnt.json'
    params = {
        'crtfc_key': crtfc_key,
        'corp_code': corp_code,
        'bsns_year': bsns_year,
    }
    data = []
    reprt_name = REPRT_NAMES.get(reprt_code, "")
    params['reprt_code'] = reprt_code
    res = self._request(api, params=params)
    if res is None:
        return None
    if res.status_code != 200:
        return None
    item = json.loads(res.text)
    if item.get('status', '') != '000':
        return None
    for _item in item.get('list'):
        _item['corp_code'] = corp_code
        _item['reprt_code'] = reprt_code
        _item['reprt_name'] = reprt_name
        data.append(_item)
    return data

get_fs(crtfc_key, '00266961', 2015, '11011')와 같이 호출하면 다음과 같은 결과를 받아올 수 있습니다.

[{'account_nm': '자산총계', 'bfefrmtrm_amount': '9,881,190,909,324', 'bfefrmtrm_dt': '2018.12.31 현재', 'bfefrmtrm_nm': '제 20 기', 'bsns_year': '2020', 'corp_code': '00266961', 'frmtrm_amount': '12,299,527,120,786', 'frmtrm_dt': '2019.12.31 현재', 'frmtrm_nm': '제 21 기', ...}, {'account_nm': '부채총계', 'bfefrmtrm_amount': '3,932,050,396,031', 'bfefrmtrm_dt': '2018.12.31 현재', 'bfefrmtrm_nm': '제 20 기', 'bsns_year': '2020', 'corp_code': '00266961', 'frmtrm_amount': '5,795,601,052,206', 'frmtrm_dt': '2019.12.31 현재', 'frmtrm_nm': '제 21 기', ...}, ...]

DART Open API 재무정보 일괄 다운로드

두 번째는 재무정보 일괄 다운로드로 한꺼번에 전체 기업의 재무제표 데이터를 받아오는 방법입니다. Open DART 사이트에서 공시정보 활용마당 / 재무정보 일괄다운로드를 클릭하면 다음과 같은 테이블을 볼 수 있습니다.

재무정보 일괄다운로드

여기서 다운로드 하면 해당 연도와 분기의 네 종류의 재무제표를 압축파일로 다운 받을 수 있습니다. 여기서는 다운로드, 압축해제를 포함하여 파이썬에서 재무제표를 읽어오는 방법을 다룹니다.

다음은 Selenium으로 다운로드 버튼을 클릭하여 파일을 다운로드하고 압축을 해제하는 코드입니다.

options = webdriver.ChromeOptions()
options.add_experimental_option("prefs", {
    "download.default_directory": self.job_base,
    "download.prompt_for_download": False,
    "download.directory_upgrade": True,
    "safebrowsing.enabled": True
})
options.add_argument("disable-infobars")
options.add_argument("--disable-extensions")
browser = webdriver.Chrome(executable_path=os.path.join(settings.QUANTYLAB_QUANT_BASE, 'drivers/chromedriver'), chrome_options=options)

try:
    browser.get('https://opendart.fss.or.kr/disclosureinfo/fnltt/dwld/main.do')
    elem = browser.find_element_by_css_selector('table.tb01')
    for elem_a in elem.find_elements_by_link_text('다운로드'):
        filename = re.findall(r'.*\(.*\'(.+)\'\).*', elem_a.get_attribute('onclick'))[0]
        # 파일 다운로드
        elem_a.click()
        time.sleep(10)
        # 압축파일 로드
        filepath = os.path.join(base, filename)
        z = zipfile.ZipFile(filepath)
        for info in z.infolist():
            # 한글 파일명 인코딩 수정
            _filename = info.filename.encode('cp437').decode('euc-kr')
            _filepath = os.path.join(base, _filename)
            if os.path.exists(_filepath):
                continue
            info.filename = _filename
            # 압축해제
            z.extract(info, base)
        z.close()
except Exception as e:
    print(str(e))
finally:
    browser.quit()

이렇게 압축해제까지 하면 2020_1분기보고서_04_현금흐름표_20210211.txt과 같은 파일들이 생성되어 있을 것입니다.

이 텍스트 파일은 다음과 같이 pandas로 쉽게 DataFrame으로 읽어올 수 있습니다. 다만 sepencoding을 다음과 같이 적절하게 정해주는 것이 중요합니다.

df = pd.read_csv(filepath, sep='\t', encoding='cp949')

여기서 당기, 당기 1분기, 당기 반기 등의 다양한 컬럼이 생기는데 저는 이들을 당기로 통합하여 정리했습니다.

다음은 포괄손익계산서의 일부 항목을 나타낸 것입니다.

항목명 결산기준일 보고서종류 당기
영업수익 2018-12-31 사업보고서 5,586,904,533,355
영업이익(손실) 2018-12-31 사업보고서 942,532,561,543
당기순이익(손실) 2018-12-31 사업보고서 627,901,873,332

그런데 재무제표를 볼 때 항목의 흐름을 보는 것이 중요합니다. 예를 들어 영업 이익이 꾸준히 증가하고 있는지를 보고 싶은 것입니다.

그래서 동일 항목에 대해서 결산기준일 순서대로 DataFrame을 재구성하고자 합니다. 이를 위해 DataFramepivot 함수를 사용합니다.

df_pivot = df.pivot(values='value', index='항목명', columns='결산기준일')

동일 항목이지만 항목명이 다르게 적혀있는 경우가 있어서 이를 맞춰주는 처리를 했습니다.

그럼 다음과 같이 동일 항목의 값을 분기 순서대로 볼 수 있습니다.

항목명 2015-12-31 2016-03-31 2016-06-30 2016-09-30 2016-12-31 2017-03-31 2017-06-30 2017-09-30 2017-12-31 2018-03-31 2018-06-30 2018-09-30 2018-12-31 2019-03-31 2019-06-30 2019-09-30 2019-12-31 2020-03-31 2020-06-30 2020-09-30
매출액 3,251,157,100,000 937,280,220,152 987,281,364,295 1,013,075,223,770 4,022,629,619,982 1,082,247,281,196 1,129,631,366,847 1,200,676,275,220 4,678,468,928,032 1,309,059,864,248 1,363,615,953,332 1,397,713,834,674 5,586,904,533,355 1,510,861,770,282 1,630,275,004,395 1,664,815,000,078 6,593,400,065,244 1,732,064,462,097 1,902,467,957,491 1,360,779,374,259
영업이익 762,203,849,000 256,827,536,675 272,663,961,847 282,298,051,525 1,102,040,475,792 290,797,289,304 285,213,522,180 312,084,238,444 1,179,187,806,331 256,980,563,870 250,583,189,279 221,718,402,565 942,532,561,543 206,244,504,954 128,334,709,480 202,086,039,366 710,070,173,513 221,495,078,328 230,597,573,115 291,726,798,004
당기순이익 516,986,279,000 165,036,146,535 213,223,506,226 198,000,157,034 759,072,951,187 210,876,050,870 171,420,229,303 215,798,264,368 770,101,670,207 153,751,654,038 281,758,577,701 68,386,763,908 627,901,873,332 87,585,903,290 27,756,852,975 85,268,053,088 396,821,062,465 134,874,581,865 90,682,203,810 235,342,965,699