Chapter 6 금융 데이터 수집하기 (심화)
6.1 수정주가 크롤링
이번 챕터에서는 수정주가, 재무제표, 가치지표를 크롤링함
국내 중소형주나 종목의 수정주가를 크롤링하기 위해 네이버 금융판을 이용함
6.1.1 개별종목 주가 크롤링
네이버 금융의 차트 탭에서 사용하는 데이터를 url에서 확인 > 날짜별 수정주가 기준의 시가, 고가, 저가, 종가, 거래량 데이터를 받아옴
이때 다른 종목의 데이터를 받아오고 싶다면 url 주소의 symbol= 뒤 티커만 변경해주면 됨
library(stringr)
# 이전 챕터에서 저장한 파일 불러오기
KOR_ticker = read.csv('data/KOR_ticker.csv', row.name = 1)
print(KOR_ticker$'종목코드'[1])
(결과) 6자리 중 앞 '00' 이 지워진 상태
[1] 5930
왼쪽에 0을 추가해 6자리로 만들어줌
KOR_ticker$'종목코드' = str_pad(KOR_ticker$'종목코드', 6, side = c('left'), pad = '0')
library(xts)
# data 폴더 내 KOR_price 폴더 생성
ifelse(dir.exists('data/KOR_price'), FALSE, dir.create('data/KOR_price'))
[1] FALSE
# 이후 loop문에서 i를 변경하여 모든 종목의 주가 다운로드
i = 1
name = KOR_ticker$'종목코드'[i] # name 에 티커를 입력
# xts 함수는 빈 시계열 데이터 생성
# Sys.Date() 함수로 현재 날짜를 기준으로 order.by
price = xts(NA, order.by = Sys.Date())
print(price)
(결과) 빈 시계열 데이터 생성 후 현재 날짜로 ordering
[,1]
2020-05-31 NA
library(httr)
library(rvest)
# name엔 위에서 받은 티커가 있음
url = paste0('https://fchart.stock.naver.com/sise.nhn?symbol=',name,'&timeframe=day&count=500&requestType=0')
# url 페이지의 데이터 불러오기
data = GET(url)
data_html = read_html(data, encoding = 'EUC-KR') %>% # HTML 정보 읽기
html_nodes('item') %>% # item 태그와
html_attr('data') # data 속성의 데이터를 추출
print(head(data_html)
(결과) | 로 구분된 날짜, 주가, 거래량 데이터가 추출됨
[1] "20180516|49200|50200|49150|49850|15918683"
[2] "20180517|50300|50500|49400|49400|10365440"
[3] "20180518|49900|49900|49350|49500|6706570"
[4] "20180521|49650|50200|49100|50000|9020998"
[5] "20180523|50600|52000|50400|51800|17095490"
[6] "20180524|52000|52000|51100|51400|8289275"
위 데이터를 테이블 형태로 변환
library(readr)
# 구분자로 이루어진 데이터를 테이블 형태로 변환시켜주는 read_delim() 함수
price = read_delim(data_html, delim = '|')
print(head(price))
(결과) 각 열은 날짜, 시가, 고가, 저가, 종가, 거래량을 의미
A tibble: 6 x 6
`20180516` `49200` `50200` `49150` `49850` `15918683`
1 20180517 50300 50500 49400 49400 10365440
2 20180518 49900 49900 49350 49500 6706570
3 20180521 49650 50200 49100 50000 9020998
4 20180523 50600 52000 50400 51800 17095490
5 20180524 52000 52000 51100 51400 8289275
6 20180525 51000 52800 50800 52700 15207266
위 데이터 중 원하는 날짜, 종가만을 선택해서 추출
library(lubridate)
library(timetk)
price = price[c(1, 5)] # 1열과 5열만 선택해 price에 저장
price = data.frame(price) # 데이터프레임으로 변형
colnames(price) = c('Date', 'Price') # 열 이름 지정
price[, 1] = ymd(price[, 1]) # ymd() 함수로 데이터 형태를 yyyy-mm-dd로 변형
price = tk_xts(price, date_var = Date) # 시계열 데이터로 변형
print(tail(price))
(결과)
Price
2020-05-22 48750
2020-05-25 48850
2020-05-26 49250
2020-05-27 49900
2020-05-28 50400
2020-05-29 50700
위 테이블을 폴더에 저장
write.csv(price, paste0('data/KOR_price/', name, '_price.csv'))
6.1.2 전 종목 주가 크롤링
위의 개별 종목 크롤링 코드에서 for loop 문을 통해 i만 변경해주면 전 종목 크롤링이 가능
library(httr)
library(rvest)
library(stringr)
library(xts)
library(lubridate)
library(readr)
# 티커 만들어주기
KOR_ticker = read.csv('data/KOR_ticker.csv', row.names = 1)
print(KOR_ticker$'종목코드'[1])
KOR_ticker$'종목코드' = str_pad(KOR_ticker$'종목코드', 6, side = c('left'), pad = '0')
# 폴더 생성
ifelse(dir.exists('data/KOR_price'), FALSE, dir.create('data/KOR_price'))
# i를 계속 변경해주면서 모든 종목의 주가 다운로드
for(i in 1:nrow(KOR_ticker)) {
price = xts(NA, order.by = Sys.Date()) # 빈 시계열 데이터 생성
name = KOR_ticker$'종목코드'[i] # 티커 부분 선택
# tryCatch() 함수는 오류 발생 시 이를 무시하고 다음 루프로 진행
tryCatch({
# url 생성
url = paste0('https://fchart.stock.naver.com/sise.nhn?symbol=',
name, '&timeframe=day&count=500&requestType=0')
# 데이터 다운로드 (개별 종목과 동일한 과정)
data = GET(url)
data_html = read_html(data, encoding = 'EUC-KR') %>%
html_nodes("item") %>%
html_attr("data")
# 데이터 나누기
price = read_delim(data_html, delim = '|')
# 필요한 열만 선택 후 데이터 클렌징
price = price[c(1, 5)] # 1, 5열만
price = data.frame(price) # df로 변환
colnames(prcie) = c('Date', 'Price') #열 이름 지정
price[, 1] = ymd(price[, 1]) # 데이터 형태 변형
rownames(price) = price[, 1] # 행 이름 지정
price[, 1] = NULL
}, error = function(e) {
# 오류 발생 시 해당 종목명을 출력하고 다음 루프로 이동
warning(paste0("Error in Ticker: ", name))
})
# 다운로드 받은 파일을 생성한 폴더 내 csv 파일로 저장
write.csv(price, paste0('data/KOR_price/', name, '_price.csv'))
# 무한 크롤링 방지 타임슬립 (2초)
Sys.sleep(2)
}
6.2 재무제표 및 가치지표 크롤링
가치지표의 경우 Company Guide 사이트에서 크롤링할 것임
6.2.1 재무제표 다운로드
http://comp.fnguide.com/SVO2/ASP/SVD_Finance.asp?pGB=1&gicode=A005930 에서 원하는 데이터를 가져올 수 있음
library(httr)
library(rvest)
# 폴더 생성
ifelse(dir.exists('data/KOR_fs'), FALSE, dir.create('data/KOR_fs'))
# 로케일 언어 영어로 설정
Sys.setlocale("LC_ALL", "English")
# GET 방식으로 url 내의 내용을 받아옴
url = paste0('http://comp.fnguide.com/SVO2/ASP/SVD_Finance.asp?pGB=1&gicode=A005930)
data = GET(url, user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) # 웹브라우저 구별 입력
# 웹 브라우저 리스트 http://www.useragentstring.com/pages/useragentstring.php 에서 확인
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'))
data = data %>%
read_html() %>% # HTML 내용 읽기
html_table() # 테이블만 추출
# 로케일 언어 한국어로 재지정
Sys.setlocale("LC_ALL", "Korean")
lapply(data, function(x) {
head(x, 3)})
(결과)
[[1]]
IFRS(연결) 2017/12 2018/12 2019/12 2020/03
1 매출액 2,395,754 2,437,714 2,304,009 553,252
2 매출원가 1,292,907 1,323,944 1,472,395 348,067
3 매출총이익 1,102,847 1,113,770 831,613 205,185
전년동기 전년동기(%)
1 523,855 5.6
2 327,465 6.3
3 196,391 4.5
[[2]]
IFRS(연결) 2019/06 2019/09 2019/12 2020/03 전년동기
1 매출액 561,271 620,035 598,848 553,252 523,855
2 매출원가 359,447 399,939 385,545 348,067 327,465
3 매출총이익 201,824 220,096 213,302 205,185 196,391
전년동기(%)
1 5.6
2 6.3
3 4.5
[[3]]
IFRS(연결) 2017/12 2018/12
1 자산 3,017,521 3,393,572
2 유동자산계산에 참여한 계정 펼치기 1,469,825 1,746,974
3 재고자산 249,834 289,847
2019/12 2020/03
1 3,525,645 3,574,575
2 1,813,853 1,867,397
3 267,665 284,549
[[4]]
IFRS(연결) 2019/06 2019/09
1 자산 3,429,401 3,533,860
2 유동자산계산에 참여한 계정 펼치기 1,734,335 1,860,421
3 재고자산 312,470 309,088
2019/12 2020/03
1 3,525,645 3,574,575
2 1,813,853 1,867,397
3 267,665 284,549
[[5]]
IFRS(연결) 2017/12 2018/12 2019/12
1 영업활동으로인한현금흐름 621,620 670,319 453,829
2 당기순손익 421,867 443,449 217,389
3 법인세비용차감전계속사업이익
2020/03
1 118,299
2 48,849
3
[[6]]
IFRS(연결) 2019/06 2019/09 2019/12
1 영업활동으로인한현금흐름 65,949 138,266 197,171
2 당기순손익 51,806 62,877 52,270
3 법인세비용차감전계속사업이익
2020/03
1 118,299
2 48,849
3
위 코드로 만들어진 재무제표 테이블 중 1, 3, 5번째 테이블만 선택
data_IS = data[[1]]
data_BS = data[[3]]
data_CF = data[[5]]
print(names(data_IS))
(결과)
[1] "IFRS(연결)" "2017/12" "2018/12"
[4] "2019/12" "2020/03" "전년동기"
[7] "전년동기(%)"
# 포괄손익계산서 테이블에서 '전년동기', '전년동기(%)'열 삭제
data_IS = data_IS[, 1:(ncol(data_IS) -2)]
데이터 클렌징
data_fs = rbind(data_IS, data_BS, data_CF) # 세 테이블 행으로 묶기
data_fs[, 1] = gsub('계산에 참여한 계정 펼치기', '', data_fs[, 1]) # gsub() 함수로 글자 삭제
data_fs = data_fs[!duplicated(data_fs[, 1]), ] # 중복되는 계정명 삭제
rownames(data_fs) = NULL # 행 이름 초기화
rownames(date_fs) = data_fs[, 1] # 열의 계정명을 행 이름으로 변경
data_fs[, 1] = NULL # 1열은 삭제
data_fs = data_fs[, substr(colnames(data_fs), 6, 7) == '12] # substr() 함수로 12월 결산 데이터(끝 글자가 12인 열) 만 선택
print(head(data_fs))
(결과)
2017/12 2018/12 2019/12
매출액 2,395,754 2,437,714 2,304,009
매출원가 1,292,907 1,323,944 1,472,395
매출총이익 1,102,847 1,113,770 831,613
판매비와관리비 566,397 524,903 553,928
인건비 67,972 64,514 64,226
유무형자산상각비 13,366 14,477 20,408
sapply(data_fs, typeof)
(결과)
2017/12 2018/12 2019/12
"character" "character" "character"
위 데이터는 문자형이므로 숫자형으로 변경해줌
library(stringr)
data_fs = sapply(data_fs, function(x) { # 각 열에 함수 적용
str_replace_all(x, ',', '') %>% # ',' 제거 후
as.numeric() # 숫자형 데이터로 변환
}) %>%
data.frame(., row.name = rownames(data_fs)) # df로 변환, 행 이름은 그대로 유지
print(head(data_fs))
(결과) 숫자형으로 변경된 데이터 프레임 형태
X2017.12 X2018.12 X2019.12
매출액 2395754 2437714 2304009
매출원가 1292907 1323944 1472395
매출총이익 1102847 1113770 831613
판매비와관리비 566397 524903 553928
인건비 67972 64514 64226
유무형자산상각비 13366 14477 20408
sapply(data_fs, typeof)
(결과)
X2017.12 X2018.12 X2019.12
"double" "double" "double"
본 게시글은 다음 강의를 참고하여 공부한 내용을 기록한 것입니다.
https://www.fastcampus.co.kr/courses/202382/clips/
I'm a Senior Student in Data Science !
데이터 사이언스를 공부하고 있는 4학년 학부생의 TIL 블로그입니다. 게시글이 도움 되셨다면 구독과 좋아요 :)
'Data Scraping > #Quant Portfolio' 카테고리의 다른 글
[퀀트] R을 활용한 퀀트 투자 포트폴리오 만들기 (5) (0) | 2020.08.25 |
---|---|
[퀀트] R을 활용한 퀀트 투자 포트폴리오 만들기 (3) (0) | 2020.08.18 |
[퀀트] R을 활용한 퀀트 투자 포트폴리오 만들기 (4) (0) | 2020.08.06 |
[퀀트] R을 활용한 퀀트 투자 포트폴리오 만들기 (2) (0) | 2020.07.30 |
[퀀트] R을 활용한 퀀트 투자 포트폴리오 만들기 (1) (0) | 2020.07.29 |