구현 사례
왜 만들었는가
- 지인의 부탁으로 만들게 되었다.
- 의뢰인은 상장사 여러 곳의 재무제표를 가져올 일이 있는데, 매번 사이트에 가서 재무제표 가져오기를 클릭하는 것이 무척 번거로워 이를 효율적으로 할 수 있는 방법이 없는지 문의해주셨다.
- 처음에는 VBA 말고 파이썬의 OpenDartReader 라이브러리를 사용해 보려고 했다. 하지만 실제로 사용을 해보니 재무제표를 가져오는 작업이 매우 매우 느리다. 도저히 속도가 답답해서 직접 코드를 작성해 보았다.
프로젝트 개요
- 유저가 업체명,연도,유형 등 필요한 사항을 기입한다.
- 코드는 이를 input 변수로 인식하고, 우선 DART OPEN API를 이용해서 해당 사업보고서의 문서정보를 가져온다. DART OPEN API의 응답 형태는 JSON이기 떄문에 문서 정보를 가져오기 위해서는 VBA-JSON 이라는 외부 라이브러리를 활용해야 한다. (https://github.com/VBA-tools/VBA-JSON)
- 문서정보가 있으면 이를 바탕으로 엑셀 파일을 DART 서버에 직접 요청해서 다운로드한다.
구현 코드
Sub download_dart()
Dim JSON As Object
Dim i, lr, year As Long
Dim apikey, doc_type, comp_code, start_date, end_date As String
Dim url1, url2, url3, url4, url5, final_url As String
Dim doc_num, download_url As String
Dim mydir, comp_name, doc_type_name As String
Dim oStream As Object
'여기에 재무제표를 저장할 폴더 경로 삽입.
mydir = "C:\Users\test\"
'여기에 개인 key 넣기 개인 Api 발급은 https://opendart.fss.or.kr/intro/main.do 이용
apikey = ""
lr = Worksheets(1).Cells(Rows.Count, 3).End(xlUp).Row
For i = 5 To lr
comp_name = Worksheets(1).Cells(i, 3).Value
comp_code = Application.WorksheetFunction.VLookup(Cells(i, 3).Value, Worksheets(2).Range("a1:e83371"), 3, False)
year = Worksheets(1).Cells(i, 4).Value
doc_type_name = Worksheets(1).Cells(i, 5).Value
doc_type = Application.WorksheetFunction.VLookup(Cells(i, 5).Value, Worksheets(3).Range("a1:d5"), 2, False) 'A003
If doc_type <> "A001" Then
start_date = CStr(year) & Application.WorksheetFunction.VLookup(Cells(i, 5).Value, Worksheets(3).Range("a1:d5"), 3, False)
end_date = CStr(year) & Application.WorksheetFunction.VLookup(Cells(i, 5).Value, Worksheets(3).Range("a1:d5"), 4, False)
Else
start_date = CStr(year + 1) & Application.WorksheetFunction.VLookup(Cells(i, 5).Value, Worksheets(3).Range("a1:d5"), 3, False)
end_date = CStr(year + 2) & Application.WorksheetFunction.VLookup(Cells(i, 5).Value, Worksheets(3).Range("a1:d5"), 4, False)
End If
url1 = "https://opendart.fss.or.kr/api/list.json?crtfc_key="
'apikey
url2 = "&pblntf_detail_ty="
'doc_type
url3 = "&corp_code="
'comp_code
url4 = "&bgn_de="
'start_date
url5 = "&end_de="
'end_date
final_url = url1 & apikey & url2 & doc_type & url3 & comp_code & url4 & start_date & url5 & end_date
Cells(i, 6).Value = final_url
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", final_url, False
.send
Set JSON = JsonConverter.ParseJson(.responseText)
End With
If JSON("message") = "조회된 데이타가 없습니다." Then
Cells(i, 6).Value = "보고서가 없습니다"
Else
Cells(i, 6).Value = "다운로드 완료"
doc_num = JSON("list")(1)("rcept_no")
download_url = "http://dart.fss.or.kr/pdf/download/excel.do?lang=ko&rcp_no=" & doc_num
Dim WinHttpReq As Object
Set WinHttpReq = CreateObject("MSXML2.XMLHTTP")
WinHttpReq.Open "GET", download_url, False
WinHttpReq.send
If WinHttpReq.Status = 200 Then
Set oStream = CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1
oStream.Write WinHttpReq.responseBody
oStream.SaveToFile mydir & comp_name & "_" & CStr(year) & "_" & doc_type_name & ".xls", 2 ' 1 = no overwrite, 2 = overwrite
oStream.Close
End If
End If
Next
End Sub
코멘트
- 엑셀 VBA로도 파이썬보다 더 사용하기 편한 프로그램을 만들 수 있다. 특히 원래 작업 자체가 엑셀 기반인 경우 VBA로 프로그램을 구현하는 것이 효율적일 수 있다. (사용자는 엑셀이라는 프로그램 안에서 크롤링 및 데이터 가공을 모두 한번에 수행할 수 있다.)]
- VBA의 Microsoft Scripting Runtime(딕셔너리 자료형태를 인식하게 해줌, VBA-JSON과 함께 쓰임)와 XML 6.0 라이브러리는 크롤링에서 정말 필수적이다. 특히 대부분의 response type이 JSON인 상황에서 이 두 라이브러리가 있으면 대부분의 크롤링 작업을 수행할 수 있다.
- Microsoft Scripting Runtime과 XML 6.0은 윈도우 엑셀에서는 기본으로 내장된 라이브러리여서, 도구-참조에 들어가서 체크를 하면 바로 사용이 가능한데 맥OS 엑셀에서는 기본으로 탑재되어 있지 않다. 맥OS로 VBA 프로그래밍이 정말 싫어지는 이유 중 하다.