출처:
퀀트투자쿡북 / 저자: 이현열: 5.1 한국거래소의 산업별 현황 및 개별지표 크롤링
(혼자한 연습)

0. 필요한 패키지 다운로드
library(rvest)
library(httr)
library(tidyverse) #dplyr을 받아야 %>%(pipeline)을 사용할 수 있음

1. 산업별 지표 크롤링

아래의 주소에서 산업별 지표를 크롤링하고 싶다고 하자.
산업별지표: http://marketdata.krx.co.kr/mdi#document=03030103

그런데 들어가보면, 이미 엑셀로 다운로드 받을 수 있는 걸 확인할 수 있다. 그러나 매번 홈페이지에 들어가 엑셀을 클릭하고 다운받아 다시 R에 올리는 것은 상당히 비효율적이다. 처음에 조금 시간을 들여서 코드를 짜면, 이를 이용해서 그때그때 곧바로 엑셀형 데이터를 csv로 받아 R까지 올리는 작업을 자동화할 수 있다.

홈페이지에 들어가 엑셀버튼을 누르면 두 가지 일이 벌어진다. (개발자모드 Network 탭에서 확인 가능)
1. GenerateOTP.jspx에서 OTP를 생성 2. download.jspx에서 OTP를 제출하고 자료를 받음

따라서 우리는 이 두 과정을 코드로 그대로 재현시켜야 한다.

1.1 url 생성 및 otp 생성
gen_otp_url <- "http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx"

gen_otp_data <-
  list(
    name = "fileDown",
    filetype = "csv",   #to save memory
    url = "MKD/03/0303/03030103/mkd03030103",
    tp_cd = "ALL",
    date = "20190809",
    lang = "ko",
    pagePath = "/contents/MKD/03/0303/03030103/MKD03030103.jsp"
  )

otp <-
  POST(
    gen_otp_url,
    query = gen_otp_data
  ) %>% 
  read_html() %>% 
  html_text

큰 틀에서 url을 요청해 전체 다운로드 후, html을 읽어내는 것은 동일.
이번에 다른 점은 query argument가 들어간다는 것. → 즉 otp 생성 시에는 쿼리 argument를 별도로 넣어줘야 함.

1.2 otp 제출 (자료 다운로드)
down_url = 'http://file.krx.co.kr/download.jspx'
down_sector = POST(down_url, query = list(code = otp),
            add_headers(referer = gen_otp_url)) %>%
  read_html() %>%
  html_text() %>%
  read_csv()

down_sector %>% head()
## # A tibble: 6 x 7
##   시장구분 종목코드 종목명  산업분류 `현재가(종가)` 전일대비 `시가총액(원)`
##   <chr>    <chr>    <chr>   <chr>             <dbl>    <dbl>          <dbl>
## 1 코스피   030720   동원수산~ 어업               9300      460    43280386500
## 2 코스피   007160   사조산업~ 어업              44650     1200   223250000000
## 3 코스피   006040   동원산업~ 어업             226000    -5500   831146866000
## 4 코스피   004970   신라교역~ 어업              12450        0   199200000000
## 5 코스피   012320   경동인베스트~ 광업              32350      350    76508494050
## 6 코스피   003580   넥스트사이언~ 광업               3105      -30    72907384095

otp 생성시와 마찬가지로 query argument가 들어가는데, 여기서 추가적으로 리스트 내 코드 구문이 들어간다. 여기서 otp를 제출한다.

1.3 관련 자료 다운로드
ifelse(dir.exists('data'), FALSE, dir.create('data'))
## [1] FALSE
write.csv(down_sector, 'data/krx_sector.csv')

2. 개별종목 지표 다운로드

똑같은 과정을 거친다.

gen_otp_url <-
  'http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx'
gen_otp_data <-
  list(
  name = 'fileDown',
  filetype = 'csv',
  url = "MKD/13/1302/13020401/mkd13020401",
  market_gubun = 'ALL',
  gubun = '1',
  schdate = '20190607',
  pagePath = "/contents/MKD/13/1302/13020401/MKD13020401.jsp")

otp <-
  POST(gen_otp_url, 
       query = gen_otp_data) %>%
  read_html() %>%
  html_text()

down_url <- 'http://file.krx.co.kr/download.jspx'
down_ind <-
  POST(down_url, 
       query = list(code = otp),
            add_headers(referer = gen_otp_url)) %>%
  read_html() %>%
  html_text() %>%
  read_csv()
print(down_ind)
## # A tibble: 2,204 x 13
##    일자       종목코드 종목명 관리여부  종가 EPS   PER   BPS   PBR  
##    <date>     <chr>    <chr>  <chr>    <dbl> <chr> <chr> <chr> <chr>
##  1 2019-06-07 000250   삼천당제약~ -        39650 409   96.94 6,719 5.9  
##  2 2019-06-07 000440   중앙에너비~ -         6880 958   7.18  7,269 0.95 
##  3 2019-06-07 001000   신라섬유~ -         2225 7     317.~ 587   3.79 
##  4 2019-06-07 001540   안국약품~ -        11350 1,154 9.84  11,2~ 1.01 
##  5 2019-06-07 001810   무림SP -         2795 505   5.53  9,212 0.3  
##  6 2019-06-07 001840   이화공영~ -         5290 24    220.~ 2,164 2.44 
##  7 2019-06-07 002230   피에스텍~ -         4275 -     -     7,283 0.59 
##  8 2019-06-07 002290   삼일기업공~ -         3185 250   12.74 4,578 0.7  
##  9 2019-06-07 002680   한탑   -         2435 -     -     2,135 1.14 
## 10 2019-06-07 002800   신신제약~ -         7050 191   36.91 3,595 1.96 
## # ... with 2,194 more rows, and 4 more variables: 주당배당금 <dbl>,
## #   배당수익률 <dbl>, `게시물 일련번호` <dbl>, 총카운트 <dbl>
write.csv(down_ind, 'data/krx_ind.csv')

3. 최근 영업일 구하기

매번 위의 코드를 돌릴때마다 날짜 부분을 수정해주는 것도 힘들다.
따라서 영업일 부분 또한 자동화를 시킬 수 있다.
그러려면 우선 영업일 정보를 가져올 수 있어야 한다.
네이버 금융에서 가져오도록 하되, 이번에는 전체 페이지를 받고, html을 읽고 분해해서 찾아내기보다, Xpath라는 부분으로 가져오도록 한다.

url <- 'https://finance.naver.com/sise/sise_deposit.nhn'

biz_day <- GET(url) %>%
  read_html(encoding = 'EUC-KR') %>%
  html_nodes(xpath =
               '//*[@id="type_1"]/div/ul[2]/li/span') %>%
  html_text() %>%
  str_match(('[0-9]+.[0-9]+.[0-9]+') ) %>%
  str_replace_all('\\.', '')

print(biz_day)
## [1] "20190807"

종합

1, 2에서 본 코드에서 date 부분을 최근 영업일로 바꿔주기만 하면 된다.

# 최근 영업일 구하기
url = 'https://finance.naver.com/sise/sise_deposit.nhn'

biz_day = GET(url) %>%
  read_html(encoding = 'EUC-KR') %>%
  html_nodes(xpath =
               '//*[@id="type_1"]/div/ul[2]/li/span') %>%
  html_text() %>%
  str_match(('[0-9]+.[0-9]+.[0-9]+') ) %>%
  str_replace_all('\\.', '')

# 산업별 현황 OTP 발급
gen_otp_url =
  'http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx'
gen_otp_data = list(
  name = 'fileDown',
  filetype = 'csv',
  url = 'MKD/03/0303/03030103/mkd03030103',
  tp_cd = 'ALL',
  date = biz_day, # 최근영업일로 변경
  lang = 'ko',
  pagePath = '/contents/MKD/03/0303/03030103/MKD03030103.jsp')
otp = POST(gen_otp_url, query = gen_otp_data) %>%
  read_html() %>%
  html_text()

# 산업별 현황 데이터 다운로드
down_url = 'http://file.krx.co.kr/download.jspx'
down_sector = POST(down_url, query = list(code = otp),
            add_headers(referer = gen_otp_url)) %>%
  read_html() %>%
  html_text() %>%
  read_csv()

#ifelse(dir.exists('data'), FALSE, dir.create('data'))
write.csv(down_sector, 'data/krx_sector.csv')

# 개별종목 지표 OTP 발급
gen_otp_url =
  'http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx'
gen_otp_data = list(
  name = 'fileDown',
  filetype = 'csv',
  url = "MKD/13/1302/13020401/mkd13020401",
  market_gubun = 'ALL',
  gubun = '1',
  schdate = biz_day, # 최근영업일로 변경
  pagePath = "/contents/MKD/13/1302/13020401/MKD13020401.jsp")

otp = POST(gen_otp_url, query = gen_otp_data) %>%
  read_html() %>%
  html_text()

# 개별종목 지표 데이터 다운로드
down_url = 'http://file.krx.co.kr/download.jspx'
down_ind = POST(down_url, query = list(code = otp),
            add_headers(referer = gen_otp_url)) %>%
  read_html() %>%
  html_text() %>%
  read_csv()

write.csv(down_ind, 'data/krx_ind.csv')

데이터 정리 부분은 나중에 직접 해보도록 한다.