Post List

2018년 7월 7일 토요일

R에서 거래소 크롤링을 통한 KOSPI 역사적 PBR 다운받기




크롤링 이전에
먼저 데이터 다운로드 과정을 살펴보면 
다음과 같습니다. 


거래소 홈페이지 http://krx.co.kr/main/main.jsp 에 접속하여
시장정보 - 지수 - 주가지수 - PBR 추이를 선택합니다.




단축코드는 80005 입니다.




크롬에서 F12 키를 눌러 개발자도구 화면을 열어줍니다.




KOSPI 계열 선택 - 일 - 1년을 선택한 후 조회를 눌러줍니다.
서버에서 데이터가 받아오는 내용이 우측 개발자 도구에 나타납니다.




CSV 를 클릭하면 data.csv 파일이 다운로드 됩니다.
우측 개발자 화면을 통해 데이터가
다운로드 받아지는 과정도 알수 있습니다.



csv 파일을 열어보면
지수 및 각 섹터별 PBR 데이터가 나옵니다.



거래소 내에서는 POST 방식을 이용하여
데이터를 받아옵니다.


구체적인 과정은 아래와 같습니다.

1. http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx
2. http://file.krx.co.kr/download.jspx : GenerateOTP.jspx를 통해 리턴받은 값을 입력으로 사용

먼저 우리가 선택한 항목을 쿼리로 보내 OTP를 생성합니다.
이를 file.krx.co.kr에 보내 해당되는 파일을 다운로드 합니다.


해당 과정을 코드로 나타내면 다음과 같습니다.


library(httr)
library(rvest)
library(tibble)
library(xts)
library(readr)
library(magrittr)
library(ggplot2)



먼저 필요한 패키지들을 열어줍니다.
이 중 httr과 rvest는 크롤링에 있어 핵심 패키지입니다.



Request URL의
http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx 
까지가 주소이며
?뒤의 값은 쿼리를 통해 생성된 주소 입니다.

해당 쿼리에 대한 자세한 사항은
하단의 Query String Parameters와 같습니다.

이러한 쿼리 스트링을 만드는 방법은
R의 httr 패키지의 post() 함수를 사용하면 됩니다.



OTP_form = POST("http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx",
                query=list(
                  name = "filedown",
                  filetype = "csv",
                  url = "MKD/13/1301/13010104/mkd13010104_02",
                  type = "kospi",
                  period_selector = "day",
                  fromdate = "20080101",
                  todate = "20180706",
                  pagePath = "/contents/MKD/13/1301/13010104/MKD13010104.jsp")
)



POST 함수의 주소 부분에는 위의 url을 입력하며
query 부분에는 해당 값의 데이터들을 list 형태로 입력해 줍니다.
fromdate는 거래소에서 제공하는 최초의 값인 20080101 을 입력합니다.




이를 통해 받아진 값 (OTP_form)은 다음과 같습니다.



OTP = content(OTP_form, "text")


이 중 text 형식만 뽑아내면 OTP를 찾을 수 있습니다.

OTP에는 다음과 같은 값이 저장됩니다.


7YlLJ8ipLYrt1U5syG24uJzl7NArQgHxKLUBvTHbTlV6sTEG15OWNfB2gZTagv0iHttEqVqhl7BsFNjEvrof2xqJosLXSTLmjGqKmYJNTreH1ZVi0epm1Roi0VU5yMILCXvWgSQHSM9IFnJrwexpvIQnxnf24sn7TLQJCXaLF1+I8AavyUZUJ0aMlYJJwMWx5e4ywoKcDS1u53fV5MrPyk8Mt9Zo9wmbvw/UXhr+iId4mF37fq5wXD3edsYdLQY5vwybsIyojaeNB9w2fO+As4LiTQe8izBGqfBoieQZu+C1M6l9VzFtmlfJ7no+NnpWgFuAwpn9360HnXC51TQD/AJhtkDjnvqxyVUupkrpoFM=



download.jspx의 Form Data를 살펴보면
위의 값과 비슷함이 확인됩니다.

(미세한 차이는 Fromdata 값의 차이에 의해 발생합니다.)



data = POST("http://file.krx.co.kr/download.jspx",
            query = list(
              code = OTP
            )
)


이를 코드로 나타내면 다음과 같습니다.

POST 함수에서
url은 http://file.krx.co.kr/download.jspx
query 부분에는 위에서 생성한 OTP 값을 입력합니다.



data_value = read_html(data$url, encoding = "UTF-8")
data_value = html_text(data_value)


data$url에는 csv 파일이 있는 링크가 저장됩니다.

"http://file.krx.co.kr/download.jspx?code=7YlLJ8ipLYrt1U5syG24uJzl7NArQgHxKLUBvTHbTlV6sTEG15OWNfB2gZTagv0iHttEqVqhl7BsFNjEvrof2xqJosLXSTLmjGqKmYJNTreH1ZVi0epm1Roi0VU5yMILCXvWgSQHSM9IFnJrwexpvIQnxnf24sn7TLQJCXaLF1%2BI8AavyUZUJ0aMlYJJwMWx5e4ywoKcDS1u53fV5MrPyk8Mt9Zo9wmbvw%2FUXhr%2BiId4mF37fq5wXD3edsYdLQY5vwybsIyojaeNB9w2fO%2BAs4LiTQe8izBGqfBoieQZu%2BC1M6l9VzFtmlfJ7no%2BNnpWgFuAwpn9360HnXC51TQD%2FAJhtkDjnvqxyVUupkrpoFM%3D"


read_html() 함수를 이용하여 이를 긁어온 후,
html_text() 함수를 이용하여 글자만 긁어오면
다음과 같은 값이 저장됩니다.





df.value =  read_csv(data_value)

read_csv() 함수를 통해 해당 데이터를 읽어오면
자동으로 tbl 형태로 저장됩니다.





이 중, 첫번째 열은 날짜이므로
행이름(rownames)로 바꾸는 것이 좋습니다.

column_to_rownames() 함수를 통해 첫번째 열을 행이름으로 변경하며,
as.xts() 함수를 통해 전체 데이터를 시계열 형태로 변경해 줍니다.


df.value = as.xts(column_to_rownames(df.value, var = colnames(df.value)[1]))







autoplot(df.value[,1])


마지막으로 ggplot2 패키지의 autoplot() 함수를 통해
그래프를 그려줍니다.

간혹 데이터가 업데이트 되지 않아
오늘자 데이터는 0 으로 표시됩니다.
그럴 경우 가장 끝행 데이터는 삭제해주면 됩니다.




OTP_form의 query 부분만 수정해주면
KRX, KOSPI. KOSDAQ 지수와
PER, PBR. DY 항목을 조정할 수 있습니다.

댓글 9개:

  1. 좋은 내용 감사드립니다.
    스마트베타 책도 구입해서 잘보고있습니다.

    답글삭제
  2. Thanks for sharing precious information. After reading your post, I've created a tool called mdkrx, which query, download, cache and transform the KRX market data. This tool is also opened in the gitlab (https://gitlab.com/yuwan694/mdkrx). And I placed a link to this page in the README.md. Cheers, Wan

    답글삭제
  3. 핸리님, 소중한 정보 공유해 주셔서 감사합니다.
    10/30일 까지 잘 사용하고 있던 code 가 다음과 같은 에러메시지와 함께 동작이 않되네요.
    stack overflow나 다른 R 관련 site를 참고해서 고쳐봐도 해결이 안되어 도움요청드립니다.

    -- Error message.

    data_value = read_html(data$url, encoding = "UTF-8")
    Error in doc_parse_raw(x, encoding = encoding, base_url = base_url, as_html = as_html, :
    Failed to parse text

    시간나실때 한번 확인해 주시면 감사하겠습니다.

    답글삭제
    답글
    1. 네 이거 거래소에서 크롤링을 막아버려서
      저희도 해결중에 있습니다....

      삭제
    2. It seems KRX now checking the 'Referer' HTML Header,
      when I added "Referer: http://marketdata.krx.co.kr/contents/MKD/13/1301/13010104/MKD13010104.jsp" HTML header to the request to the download URL, it started working.

      in python code:
      def _cache_miss(self, a_cache, a_cache_key):
      my_download_input = {'code': self.otp_code}
      my_html_headers = { # 'Origin': 'http://marketdata.krx.co.kr',
      # 'Upgrade-Insecure-Requests': '1',
      'Referer': 'http://marketdata.krx.co.kr' + self.the_otp_input['pagePath']}
      my_request = requests.post(self.the_download_url, my_download_input, headers=my_html_headers)
      a_cache[a_cache_key] = my_request.content

      Cheers,

      삭제
    3. Sorry, not HTML header, but HTTP Header.
      Write up some details in here (https://gitlab.com/yuwan694/mdkrx/blob/master/docs/referer_http_header.md).

      삭제
    4. @유완
      Thank you! It works!
      Took almost 3 days to fix this...!

      삭제
  4. It seems KRX start checking User-Agent HTTP header as well. If things are not working, try adding User-Agent HTTP header as Mozilla/5.0. See "https://auslife8261.blogspot.com/2019/12/mdkrx-krx.html" for more details. Cheers,

    답글삭제