Dart에서 원하는 기업의 재무제표를 다운로드하기

아래의 두 코드를 기반으로 내 목적에 맞게 수정하였다.

  1. 퀀트투자쿡북: 네이버금융에서 주식티커 크롤링
  2. Henry’s Quantopia: R에서 전자공시시스템(DART) API를 이용한 크롤링

Dart는 취준생, 현업자 관계없이 자주 사용하는 사이트이다. 기업의 정기/분기/반기 보고서 또한 여기에 수록되는데, 홈페이지에서 직접 다운로드 받알 수 있으나 너무 오랜 시간이 소요된다.

(사진출처: http://henryquant.blogspot.com/2019/02/r-dart-api.html)

따라서 아래의 코드를 실행시키면 저절로 10년치 자료가 받게 하는 걸 목적으로 한다.

report_down("하이닉스")


(받아진 파일 리스트)


(개별 파일을 열었을 때)

추가적으로 몇 가지 변형 또한 할 수 있다.

report_down("하이닉스") #10년치 정기공시
report_down("하이닉스", type=1) #10년치 정기공시(default)
report_down("하이닉스", type=2) #10년치 반기공시
report_down("하이닉스", type=3) #10년치 분기공시

report_down("현대", type=1) #다운로드 수행하지 않음 #'현대'라는 이름으로는 여러 종목코드 존재
report_down("현대", type=1, force=T) #여러 종목코드 중에서 첫 번째 종목의 보고서를 다운받게 할 수 있음

단계별로 살펴보도록 하자.

  1. 필요패키지
library(rvest)
library(httr)
library(dplyr)
library(jsonlite)
  1. 네이버금융에서 주식티커 크롤링

아래 function을 실행하면 ticker_list라는 종목코드 리스트를 다운받고자 한다.

download()

아래는 function의 코드다.

download <- function() {
  data <- list()                                          #1. 전체 내용이 최종적으로 담기게 될 empty list 생성
  
 
  for (i in 0:1) {                                        #2. (i=0 코스피) (i=1 코스닥) 리스트에 각각 담고 나중에 do.call rbind로 합칠 예정
    
    ticker <- list()                                      #1) 페이지별 정보가 바로 이 리스트에 담기게 됨
    url <-                                                #(1) 코스피/코스닥 종목리스트 첫 페이지 url           
      paste0('https://finance.naver.com/sise/',
             'sise_market_sum.nhn?sosok=',i,'&page=1')
    
    down_table = GET(url) 
    
                                                          #(2) 최종 페이지 번호 찾아주기
    navi.final <-
      read_html(down_table, encoding = "EUC-KR") %>%
      html_nodes(., ".pgRR") %>%
      html_nodes(., "a") %>%
      html_attr(.,"href") %>%                             #여기에 번호가 숨겨져 있음
      strsplit(., "=") %>%                                #번호만 뽑아내는 작업
      unlist() %>%
      tail(., 1) %>%
      as.numeric()
    
                                                           
    for (j in 1:navi.final) {                             #3. 알아낸 최종번호로 페이지별 정보를 크롤링하기
      
                                                          # 각 페이지에 해당하는 url 생성
      url <- paste0(
        'https://finance.naver.com/sise/',
        'sise_market_sum.nhn?sosok=',i,"&page=",j)
      down_table <- GET(url)
      
      Sys.setlocale("LC_ALL", "English")
      # 한글 오류 방지를 위해 영어로 로케일 언어 변경
      
      table <- read_html(down_table, encoding = "EUC-KR") %>%
        html_table(fill = TRUE)
      table <- table[[2]] # 원하는 테이블 추출
      
      Sys.setlocale("LC_ALL", "Korean")
      # 한글을 읽기위해 로케일 언어 재변경
      
      table[, ncol(table)] = NULL # 토론식 부분 삭제
      table <- na.omit(table) # 빈 행 삭제
      
      # 6자리 티커만 추출
      symbol <- read_html(down_table, encoding = "EUC-KR") %>%
        html_nodes(., "tbody") %>%
        html_nodes(., "td") %>%
        html_nodes(., "a") %>%
        html_attr(., "href")
      
      symbol <- sapply(symbol, function(x) {
        substr(x, nchar(x) - 5, nchar(x)) 
      }) %>% unique() #sapply: 배열데이터 계산
      
      # 테이블에 티커 넣어준 후, 테이블 정리
      table$N = symbol
      colnames(table)[1] = "종목코드"
      
      rownames(table) = NULL
      ticker[[j]] = table #페이지별 테이블을 ticker 리스트에 담아줌
      
      Sys.sleep(0.5) # 페이지 당 0.5초의 슬립 적용
    }
    
    # do.call을 통해 리스트를 데이터 프레임으로 묶기
    ticker = do.call(rbind, ticker)     #4. ticker 리스트에는 개별 cart마다 코스닥 or 코스피 페이지별 테이블이 담겨 있음. 그것을 하나로 합치는 것임
    data[[i + 1]] = ticker              #5. 첫째 cart에는 코스피 페이지별 테이블이 모두 합쳐진 data.frame이, 두 번째는 코스닥 자료가 들어감
  }
  
  # 코스피와 코스닥 테이블 묶기
  data <- do.call(rbind, data)          #6. 두 data.frame을 묶어줌
  assign("ticker_list", data, .GlobalEnv) #7. 최종결과물을 저장함
}

최종적으로 아래와 같은 결과물을 만들 수 있음.

download()
ticker_list %>% head()
##   종목코드     종목명  현재가 전일비 등락률 액면가  시가총액 상장주식수
## 1   005930   삼성전자  44,000    600 +1.38%    100 2,626,704  5,969,783
## 2   000660 SK하이닉스  77,400  4,100 +5.59%  5,000   563,474    728,002
## 3   005935 삼성전자우  36,850    850 +2.36%    100   303,234    822,887
## 4   005380     현대차 128,500  2,000 +1.58%  5,000   274,564    213,668
## 5   035420      NAVER 146,500  1,500 +1.03%    100   241,452    164,813
## 6   012330 현대모비스 248,500  4,500 +1.84%  5,000   236,837     95,307
##   외국인비율    거래량   PER   ROE
## 1      57.44 8,784,598  7.30 19.63
## 2      51.60 3,802,918  3.63 38.53
## 3      93.12 1,313,358  6.12   N/A
## 4      44.13   405,745 24.01  2.20
## 5      59.15   370,593 37.21 12.97
## 6      49.26   173,024 12.81  6.30
  1. 종목코드 검색하기

받아온 ticker_list에서 원하는 기업의 종목코드를 검색할 수 있다.

find_ticker <- 
  function(name) {
    search_result <- ticker_list[grepl(name, ticker_list$종목명, fixed = T), 1:2]
    print(search_result)
  }
find_ticker("하이닉스")
##   종목코드     종목명
## 2   000660 SK하이닉스
find_ticker("삼성전자")
##   종목코드     종목명
## 1   005930   삼성전자
## 3   005935 삼성전자우
find_ticker("현대")
##      종목코드                    종목명
## 4      005380                    현대차
## 6      012330                현대모비스
## 43     086280              현대글로비스
## 46     267250            현대중공업지주
## 48     004020                  현대제철
## 52     000720                  현대건설
## 75     005387                현대차2우B
## 100    017800              현대엘리베이
## 101    001450                  현대해상
## 108    005385                  현대차우
## 111    069960                현대백화점
## 119    010620              현대미포조선
## 122    294870           HDC현대산업개발
## 128    064350                  현대로템
## 149    011210                  현대위아
## 154    005440              현대그린푸드
## 155    307950              현대오토에버
## 157    057050                현대홈쇼핑
## 159    011200                  현대상선
## 234    267270              현대건설기계
## 242    006390            한일현대시멘트
## 294    126560            현대에이치씨엔
## 365    001500                현대차증권
## 372    079430                현대리바트
## 394    267260              현대일렉트릭
## 402    011760                  현대상사
## 485    005389                현대차3우B
## 527    004310                  현대약품
## 540    089470                 HDC현대EP
## 572    004560            현대비앤지스틸
## 647    227840      현대코퍼레이션홀딩스
## 1148   000725                현대건설우
## 1410   004565          현대비앤지스틸우
## 1425   138540 TIGER 현대차그룹+펀더멘털
## 1621   048410                현대바이오
## 2255   016790                  현대사료
## 2333   039010                  현대통신
## 2600   170030                  현대공업
  1. 기업명을 통해 보고서 다운로드 받기

제일 처음에 언급했던 것과 마찬가지로, 코드를 실행시키면 자동으로 보고서가 다운로드 받게 만들고 싶다.
아래는 함수의 코드다.

report_down <-
  function(name, type = 1, force = F) {
    search_result <- ticker_list[grepl(name, ticker_list$종목명, fixed = T), 1:2]
    
    api.key <- '직접 스스로 받은 API키를 치세요'
    start.date <- '19990101'
    ticker <- search_result[1, 1]
    url <- paste0("http://dart.fss.or.kr/api/search.json?auth=",
                  api.key,"&crp_cd=",ticker,"&start_dt=",start.date,
                  "&bsn_tp=A00", type)
    # auth  발급받은 인증키(40자리)(필수)
    # crp_cd    공시대상회사의 종목코드(상장사:숫자 6자리) 또는 고유번호(기타법인:숫자 8자리)
    # start_dt  검색시작 접수일자(YYYYMMDD) : 없으면 end_dt
    # bsn_tp    #A001: 정기 #A002: 반기 #A003: 분기
    
    setwd("자신이 파일을 다운받을 경로를 치세요")
    if (nrow(search_result) == 1) {
      
      print(search_result)
      
      #data download
      data <- fromJSON(url)
      data.df <- data$list
      data.df.rcp <- data.df$rcp_no #보고서번호
      
      #Excel download
      for (i in 1:10) {
        
        url.business.report = paste0('http://dart.fss.or.kr/dsaf001/main.do?rcpNo=',data.df.rcp[i])
        req <- GET(url.business.report)
        req <- read_html(req) %>% html_node(xpath = '//*[@id="north"]/div[2]/ul/li[1]/a')
        
        req = req %>% html_attr('onclick')
        dcm = stringr::str_split(req, ' ')[[1]][2] %>% readr::parse_number()
        
        
        query.base = list(
          rcp_no = data.df.rcp[i],
          dcm_no = dcm,
          lang = "ko" #이게 없으면 영문으로 다운로드 됨 (내 경우에만 그럴수도)
        )
        
        down.excel = POST('http://dart.fss.or.kr/pdf/download/excel.do',
                          query = query.base)
        
        
        writeBin(content(down.excel, "raw"), 
                 paste0(ticker, "_", data.df.rcp[i], '.xls'))  
      }
      
    } else if (nrow(search_result) == 0) {
      
      print("invalid company name")
      
    } else {
      
      if (force == F) {
        print("multiple result: force = T to force downloading")
        print(search_result)
      } else {
        print("multiple result: downloaded forcefully the first company")
        print(search_result)
        
        #data download
        data <- fromJSON(url)
        data.df <- data$list
        data.df.rcp <- data.df$rcp_no #보고서번호
        
        #Excel download
        for (i in 1:10) {
          
          url.business.report = paste0('http://dart.fss.or.kr/dsaf001/main.do?rcpNo=',data.df.rcp[i])
          req <- GET(url.business.report)
          req <- read_html(req) %>% html_node(xpath = '//*[@id="north"]/div[2]/ul/li[1]/a')
          
          req = req %>% html_attr('onclick')
          dcm = stringr::str_split(req, ' ')[[1]][2] %>% readr::parse_number()
          
          
          query.base = list(
            rcp_no = data.df.rcp[i],
            dcm_no = dcm,
            lang = "ko" #이게 없으면 영문으로 다운로드 됨 (내 경우에만 그럴수도)
          )
          
          down.excel = POST('http://dart.fss.or.kr/pdf/download/excel.do',
                            query = query.base)
          
          writeBin(content(down.excel, "raw"), 
                   paste0(ticker, "_", data.df.rcp[i], '.xls'))  
      }
      
      }
      
    }
  }

몇 가지 예를 보겠다.

report_down("하이닉스") #정기공시 10건 다운로드
##   종목코드     종목명
## 2   000660 SK하이닉스
report_down("하이닉스", 2) #분기공시 10건 다운로드
##   종목코드     종목명
## 2   000660 SK하이닉스
report_down("현대") #다운로드되지 않음
## [1] "multiple result: force = T to force downloading"
##      종목코드                    종목명
## 4      005380                    현대차
## 6      012330                현대모비스
## 43     086280              현대글로비스
## 46     267250            현대중공업지주
## 48     004020                  현대제철
## 52     000720                  현대건설
## 75     005387                현대차2우B
## 100    017800              현대엘리베이
## 101    001450                  현대해상
## 108    005385                  현대차우
## 111    069960                현대백화점
## 119    010620              현대미포조선
## 122    294870           HDC현대산업개발
## 128    064350                  현대로템
## 149    011210                  현대위아
## 154    005440              현대그린푸드
## 155    307950              현대오토에버
## 157    057050                현대홈쇼핑
## 159    011200                  현대상선
## 234    267270              현대건설기계
## 242    006390            한일현대시멘트
## 294    126560            현대에이치씨엔
## 365    001500                현대차증권
## 372    079430                현대리바트
## 394    267260              현대일렉트릭
## 402    011760                  현대상사
## 485    005389                현대차3우B
## 527    004310                  현대약품
## 540    089470                 HDC현대EP
## 572    004560            현대비앤지스틸
## 647    227840      현대코퍼레이션홀딩스
## 1148   000725                현대건설우
## 1410   004565          현대비앤지스틸우
## 1425   138540 TIGER 현대차그룹+펀더멘털
## 1621   048410                현대바이오
## 2255   016790                  현대사료
## 2333   039010                  현대통신
## 2600   170030                  현대공업
report_down("현대", force=T) #첫번째 종목 다운로드(현대자동차)
## [1] "multiple result: downloaded forcefully the first company"
##      종목코드                    종목명
## 4      005380                    현대차
## 6      012330                현대모비스
## 43     086280              현대글로비스
## 46     267250            현대중공업지주
## 48     004020                  현대제철
## 52     000720                  현대건설
## 75     005387                현대차2우B
## 100    017800              현대엘리베이
## 101    001450                  현대해상
## 108    005385                  현대차우
## 111    069960                현대백화점
## 119    010620              현대미포조선
## 122    294870           HDC현대산업개발
## 128    064350                  현대로템
## 149    011210                  현대위아
## 154    005440              현대그린푸드
## 155    307950              현대오토에버
## 157    057050                현대홈쇼핑
## 159    011200                  현대상선
## 234    267270              현대건설기계
## 242    006390            한일현대시멘트
## 294    126560            현대에이치씨엔
## 365    001500                현대차증권
## 372    079430                현대리바트
## 394    267260              현대일렉트릭
## 402    011760                  현대상사
## 485    005389                현대차3우B
## 527    004310                  현대약품
## 540    089470                 HDC현대EP
## 572    004560            현대비앤지스틸
## 647    227840      현대코퍼레이션홀딩스
## 1148   000725                현대건설우
## 1410   004565          현대비앤지스틸우
## 1425   138540 TIGER 현대차그룹+펀더멘털
## 1621   048410                현대바이오
## 2255   016790                  현대사료
## 2333   039010                  현대통신
## 2600   170030                  현대공업
report_down("횬대") #다운로드되지 않음
## [1] "invalid company name"
  1. 데이터 읽어오기

Working directory에 파일이 그대로 있다는 것을 가정하면, list.files 함수를 이용해 파일을 가져올 수 있다.

list.files() %>% head() #working directory에 저장된 파일 목록 확인
## [1] "000660_20110829000354.xls" "000660_20111123000206.xls"
## [3] "000660_20120817000001.xls" "000660_20130814001050.xls"
## [5] "000660_20140331002980.xls" "000660_20140814001611.xls"
df <- readxl::read_excel(list.files()[1], sheet = 2) #그 중에서 첫 번째 파일 읽어오기
## New names:
## * `` -> ...2
## * `` -> ...3
df
## # A tibble: 51 x 3
##    `연결 재무상태표`                   ...2            ...3      
##    <chr>                               <chr>           <chr>     
##  1 제 64 기 반기말 2011.06.30 현재     <NA>            <NA>      
##  2 제 63 기말          2010.12.31 현재 <NA>            <NA>      
##  3 (단위 : 백만원)                     <NA>            <NA>      
##  4 <NA>                                제 64 기 반기말 제 63 기말
##  5 자산                                <NA>            <NA>      
##  6 유동자산                            5172058         5416086   
##  7 현금및현금성자산                    747956          1253226   
##  8 단기금융상품                        1031863         948476    
##  9 매출채권                            1663852         1604952   
## 10 기타수취채권                        254822          167684    
## # ... with 41 more rows