출처:
퀀트투자쿡북 / 저자: 이현열: 4.2.3 네이버 금융에서 주식티커 크롤링
(혼자한 연습)

네이버 금융 주식티커 크롤링

목표: 네이버 금융에서 모든 상장기업 티커(6자리 ID) 긁어오기
이번 연습은 조금 어렵다. 해야 할 일이 여러 개다.

코스피
코스닥

  1. 코스피에서 티커를 긁어온다.
  1. 코스닥에서 티커를 긁어온다.
  1. 데이터를 정리하고 합치는 작업을 수행한다.

상당히 난해할 수 있으므로, 개별 작업을 하나하나 살펴본 후 마지막에 전부 합친다.


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

1. 모든 페이지의 정보를 받아오는 코드

i <- 0 #i = 1: 코스닥
       #일단 코스피부터, 나중에 loop 돌리면 되니까

url <- paste0('https://finance.naver.com/sise/',
             'sise_market_sum.nhn?sosok=',i,'&page=1') #page=1: 첫페이지 (나중에 이 부분을 변수로 바꾸고, 
                                                       #loop을 돌리면 모든 페이지 정보를 받음)
down_table <- GET(url)

down_table #charset=EUR-KR (인코딩)
## Response [https://finance.naver.com/sise/sise_market_sum.nhn?sosok=0&page=1]
##   Date: 2019-08-10 16:41
##   Status: 200
##   Content-Type: text/html;charset=EUC-KR
##   Size: 95.6 kB
## 
## 
## 
## 
## 
## 
## 
## <!--  global include -->
## 
##  
## ...

2. 마지막 페이지가 몇인지 알아오는 코드

navi.final = 
      read_html(down_table, encoding = 'EUC-KR') %>%
      html_nodes(., '.pgRR') %>%
      html_nodes(., 'a') %>%
      html_attr(., 'href')

navi.final
## [1] "/sise/sise_market_sum.nhn?sosok=0&page=31"

navi.final 에서 마지막 두 자리가 곧 페이지 마지막 번호가 됨. 따라서 코스피의 경우 31임을 알 수 있음

navi.final <-
  navi.final %>% 
  str_split(., "=") %>% 
  unlist() %>% 
  tail(., 1) %>% 
  as.numeric()

navi.final
## [1] 31

3. 개별 페이지에서 테이블을 받아오는 코드

Sys.setlocale("LC_ALL", "English") #오류 방지를 위해 시스템 언어를 영어로 수정

table <-
  read_html(down_table, encoding = "EUC-KR") %>% #전체 내용에서 일단 html을 읽어내야 함
  html_table(fill = TRUE) #table 식 자료는 이 코드다 필요
table <- table[[2]] #list 형에서 이 부분이 바로 얻어오고자 하는 테이블임

Sys.setlocale("LC_ALL", "Korean") #다시 한국어로 변경
print(head(table))
##    N     종목명  현재가 전일비 등락률 액면가  시가총액 상장주식수
## 1 NA                                                             
## 2  1   삼성전자  43,150    500 +1.17%    100 2,575,961  5,969,783
## 3  2 SK하이닉스  73,300  1,000 +1.38%  5,000   533,626    728,002
## 4  3 삼성전자우  35,500    100 +0.28%    100   292,125    822,887
## 5  4     현대차 132,500  2,500 +1.92%  5,000   283,110    213,668
## 6  5      NAVER 142,500  2,500 +1.79%    100   234,859    164,813
##   외국인비율    거래량   PER   ROE 토론실
## 1         NA            <NA>  <NA>     NA
## 2      57.61 9,076,689  7.16 19.63     NA
## 3      51.51 2,058,467  3.43 38.53     NA
## 4      92.95   717,379  5.89   N/A     NA
## 5      44.31   512,312 24.76  2.20     NA
## 6      59.14   367,996 36.20 12.97     NA

4. 받아온 테이블을 정리하는 코드

table[, ncol(table)] = NULL #마지막 열은 필요 없으므로 날려버리자 (토론실)
table <- na.omit(table) #자료가 없는 행은 날려버리자(Missing value 문제가 아니고, 컴퓨터 코드 상 자료가 없는 부분이 이렇게 처리 돼 있음)
table %>% head()
##    N     종목명  현재가 전일비 등락률 액면가  시가총액 상장주식수
## 2  1   삼성전자  43,150    500 +1.17%    100 2,575,961  5,969,783
## 3  2 SK하이닉스  73,300  1,000 +1.38%  5,000   533,626    728,002
## 4  3 삼성전자우  35,500    100 +0.28%    100   292,125    822,887
## 5  4     현대차 132,500  2,500 +1.92%  5,000   283,110    213,668
## 6  5      NAVER 142,500  2,500 +1.79%    100   234,859    164,813
## 10 6 현대모비스 243,500  7,000 +2.96%  5,000   232,072     95,307
##    외국인비율    거래량   PER   ROE
## 2       57.61 9,076,689  7.16 19.63
## 3       51.51 2,058,467  3.43 38.53
## 4       92.95   717,379  5.89   N/A
## 5       44.31   512,312 24.76  2.20
## 6       59.14   367,996 36.20 12.97
## 10      49.09   190,639 12.55  6.30

5. 6자리 티커 받아오기

  • 티커를 받아와서 테이블에 접붙일 것임
  • 코스피 첫페이지 기준
share_id <-
  read_html(down_table, encoding = "EUC-KR") %>% 
  html_nodes(., 'tbody') %>%
  html_nodes(., 'td') %>%
  html_nodes(., 'a') %>%
  html_attr(., 'href')

share_id %>% head()
## [1] "/item/main.nhn?code=005930"  "/item/board.nhn?code=005930"
## [3] "/item/main.nhn?code=000660"  "/item/board.nhn?code=000660"
## [5] "/item/main.nhn?code=005935"  "/item/board.nhn?code=005935"
  • 끝에 6자리만 갈라쳐 내면 됨
share_id <-
  sapply(share_id, function(x) {
        substr(x, nchar(x) - 5, nchar(x)) 
      })

share_id <- unique(share_id)

share_id %>% head(10)
##  [1] "005930" "000660" "005935" "005380" "035420" "012330" "051910"
##  [8] "068270" "017670" "055550"

6. 티커를 테이블에 접붙여 테이블을 완성시키는 코드

table$N <- share_id
colnames(table)[1] = '종목코드'
rownames(table) = NULL
table %>% head()
##   종목코드     종목명  현재가 전일비 등락률 액면가  시가총액 상장주식수
## 1   005930   삼성전자  43,150    500 +1.17%    100 2,575,961  5,969,783
## 2   000660 SK하이닉스  73,300  1,000 +1.38%  5,000   533,626    728,002
## 3   005935 삼성전자우  35,500    100 +0.28%    100   292,125    822,887
## 4   005380     현대차 132,500  2,500 +1.92%  5,000   283,110    213,668
## 5   035420      NAVER 142,500  2,500 +1.79%    100   234,859    164,813
## 6   012330 현대모비스 243,500  7,000 +2.96%  5,000   232,072     95,307
##   외국인비율    거래량   PER   ROE
## 1      57.61 9,076,689  7.16 19.63
## 2      51.51 2,058,467  3.43 38.53
## 3      92.95   717,379  5.89   N/A
## 4      44.31   512,312 24.76  2.20
## 5      59.14   367,996 36.20 12.97
## 6      49.09   190,639 12.55  6.30

7. 완성시키기

  • 현재까지 코스피 첫페이지를 받아와서 정리시키는 코드를 1부터 6까지 살펴보았음
  • loop을 두 번 돌리면 개별 테이블을 모두 받을 수 있음
    1. 코스피/코스닥: 먼저 코스피를, 그 뒤로는 코스닥을 돌림
    2. 페이지별: 먼저 코스피를, 그 내에서 loop이 돌아가 페이지별 뽑고, 다시 첫째 loop으로 돌아가 반복
  • 주의할 점은, 개별 테이블이 들어갈 list를 만들어야 하는 과제가 남았다는 점
  • 이에 더해, 그 list를 하나로 묶어서 단일 data.frame으로 만들어줘야 한다.
data = list() #여기에 최종 결과가 담기게 될 것

# i = 0 은 코스피, i = 1 은 코스닥 
for (i in 0:1) { #큰 loop (코스피 & 코스닥)

  ticker = list() #첫번째는 코스피 페이지별 테이블이, 두 번째는 코스닥 페이지별 테이블이 담기게 될 것
  url =           #일단 모든 페이지 정보를 담아온다             
    paste0('https://finance.naver.com/sise/',
             'sise_market_sum.nhn?sosok=',i,'&page=1')
  
  down_table = GET(url)
  
  # 최종 페이지 번호 찾아주기
  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 loop를 이용하여 테이블 추출하기
  for (j in 1:navi.final) {
    
    # 각 페이지에 해당하는 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()
    
    # 테이블에 티커 넣어준 후, 테이블 정리
    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) #ticker 리스트에는 개별 cart마다 코스닥 or 코스피 페이지별 테이블이 담겨 있음. 그것을 하나로 합치는 것임
  data[[i + 1]] = ticker #첫째 cart에는 코스피 페이지별 테이블이 모두 합쳐진 data.frame이, 두 번째는 코스닥 자료가 들어감
}

# 코스피와 코스닥 테이블 묶기
data = do.call(rbind, data) #두 data.frame을 묶어줌

data %>% head()
##   종목코드     종목명  현재가 전일비 등락률 액면가  시가총액 상장주식수
## 1   005930   삼성전자  43,150    500 +1.17%    100 2,575,961  5,969,783
## 2   000660 SK하이닉스  73,300  1,000 +1.38%  5,000   533,626    728,002
## 3   005935 삼성전자우  35,500    100 +0.28%    100   292,125    822,887
## 4   005380     현대차 132,500  2,500 +1.92%  5,000   283,110    213,668
## 5   035420      NAVER 142,500  2,500 +1.79%    100   234,859    164,813
## 6   012330 현대모비스 243,500  7,000 +2.96%  5,000   232,072     95,307
##   외국인비율    거래량   PER   ROE
## 1      57.61 9,076,689  7.16 19.63
## 2      51.51 2,058,467  3.43 38.53
## 3      92.95   717,379  5.89   N/A
## 4      44.31   512,312 24.76  2.20
## 5      59.14   367,996 36.20 12.97
## 6      49.09   190,639 12.55  6.30
data %>% tail()
##      종목코드              종목명 현재가 전일비 등락률 액면가 시가총액
## 2884   258790    케이비제11호스팩  2,225     15 -0.67%    100       71
## 2885   323210 이베스트이안스팩1호  2,150      5 -0.23%    100       69
## 2886   264290   한화에이스스팩3호  2,195     15 +0.69%    100       68
## 2887   021045        대호피앤씨우  1,320      0  0.00%    500       56
## 2888   032685          소프트센우 23,200    850 +3.80%    500       33
## 2889   149940                모다    155      0  0.00%    500       33
##      상장주식수 외국인비율  거래량       PER     ROE
## 2884      3,210       0.00   5,005    370.83    0.33
## 2885      3,200       1.19 144,606       N/A     N/A
## 2886      3,080       0.12   9,310    439.00    0.28
## 2887      4,242       0.07  12,283     47.14     N/A
## 2888        143       0.15   3,228 -1,288.89     N/A
## 2889     21,160       0.94       0     -0.32 -105.84