국토교통부 실거래가 데이터 크롤링 코드

국토교통부 실거래가 데이터 스크래핑 코드를 공유한다. 블로그에 썼던 데이터 분석에 사용한 데이터는 친분이 있는 분으로 부터 받은 데이터인데, 새로운 매매 데이터가 올라가면서 매번 요청하기 힘들것 같아서 코드를 작성해 봤고, 아래와 같이 간단하게 스크래핑 코드를 만들 수 있었다.

데이터 스크래핑 코드는 항상 그렇듯이 임시방편적이고, 코드가 깨끗하지 않다. 그래서 좀 색다르게 magrittr 방식으로 코딩해 봤으나 그다지 나아 보이지 않는다. ;;

수백만건의 데이터를 아래와 같은 간단한 코드로 수집할 수 있다는게 데이터 크롤링의 재미가 아닐까 생각하며, 다시한번 rvest, stringi, XLConnect, data.table과 같은 유용한 패키지의 존재에 감사한다.

library(rvest)
library(httr)
library(stringi)
library(XLConnect)
library(data.table)

## 엑셀 데이터 다운로딩 부분
# 데이터는 working dir아래 data 디렉토리에 쌓인다. 따리서 data 디렉토리를 만들어 주자!

su <- file("succ.txt", "w")

agent_nm <-
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:35.0) Gecko/20100101 Firefox/35.0"

maxidx <- html('http://rt.molit.go.kr/rtFile.do?cmd=list') %>%
  html_nodes('.notiWhite1 .notiBorad01') %>%
  html_text %>% as.numeric %>% max

for(i in maxidx:1){
  urls_view <- sprintf("http://rt.molit.go.kr/rtFile.do?cmd=view&seqNo=%d", i)
  r <- GET(urls_view,
           user_agent(agent_nm))
  htxt <- html(r, "text")

  if((html_nodes(htxt, "td.notiBorad14")[[2]]  %>%
        html_text()  %>%
        stri_trim_both) == '첨부파일이 존재하지 않습니다.') next

  download_tags <- html_nodes(htxt, "td.notiBorad14")[[2]] %>%
    html_nodes('a[href^="javascript:jsDown"]')

  for(dtag in download_tags){

    dtag %>% html_attr("href") %>%
      stri_match_all_regex(pattern="javascript:jsDown\\('([0-9]+)','([0-9]+)'\\);") %>%
      .[[1]]  %>%
          {
            f_idx <<- .[2] %>% as.numeric
            s_idx <<- .[3] %>% as.numeric
          }

    f_nm <- dtag %>% html_text

    urls <- sprintf("http://rt.molit.go.kr/rtFile.do?cmd=fileDownload&seq_no=%d&file_seq_no=%d", f_idx,s_idx)
    r <- GET(urls,
             user_agent(agent_nm))
    bin <- content(r, "raw")

    #1kb 미만의 데이터는 버림(에러 페이지?)
    if(length(bin) < 1000) next
    writeBin(bin, sprintf("data/%s",f_nm))
    cat(sprintf("%d, %d\n", f_idx,s_idx), file = su)
    print(sprintf("%d, %d", f_idx,s_idx))
  }
}

close(su)




## 엑셀 데이터에서 테이블을 추출해 하나의 아파트 매매 데이터로 통합하는 코드
## 연립 다세대, 단독 다가 데이터도 간단한 코드 변환으로 통합할 수 있다.


f_list <- list.files('data') %>%
  stri_trans_nfc  %>%
  .[stri_detect_fixed(.,'매매아파트')]

total_list <- list()

#파일의 각 시트(지역)를 data.table로 변환하고 필요 필드 추가함
for(xlsf in f_list){
  wb <- loadWorkbook(paste0('data/',xlsf))
  sells <- list()
  fname <- stri_replace_last_fixed(xlsf, '.xls',"")
  yyyymm <- substring(fname, 1, 6)
  typenm <- substring(fname, 9)
  for(nm in getSheets(wb)) {
    df <- data.table(readWorksheet(wb, sheet = nm, header = TRUE))
    df[,`:=`(region=nm, yyyymm=yyyymm, typenm=typenm)]
    df[,`거래금액.만원.`:= stri_replace_all_fixed(`거래금액.만원.`, ',', '')]
    sells[[nm]] <- df
  }
  total_list[[paste0(yyyymm,typenm)]] <- rbindlist(sells)
}

result_sales_dt <- rbindlist(total_list)

#결과 데이터 저장
save(result_sales_dt, file='result_sales_dt.RData')

실거래가 페이지가 바뀌지 않는 이상 동작을 할 것이고, result_sales_dt객체에 2015년 1월 기준 4,964,004건의 매매데이터가 쌓여 있는 것을 확인해 볼 수 있을 것이다.

사족이나 그동안 스크래핑은 Python과 같은 다른 언어를 사용하거나 webkit 라이브러리를 이용해 브라우저 객체를 통해 스크래핑 하곤 했는데, 이젠 여러 패키지 덕분에 R에서도 매우 간단해졌다. 10년 전만해도 HTTP 프로토콜 책을 옆에 끼고 크롤러 코딩을 했었는데, 격세지감을 느낄 따름이다.

윈도우에서 동작여부가 매우 궁금하나, 이런 부분에 대해서는 case by case라 시간 소모하고 싶지 않다.

그러나 참고를 위해서 코드가 동작하는 환경 정보는 추가한다.

## R version 3.1.2 (2014-10-31)
## Platform: x86_64-apple-darwin13.4.0 (64-bit)
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base
##
## other attached packages:
## [1] RWordPress_0.2-3    knitr_1.9           XLConnect_0.2-10
## [4] XLConnectJars_0.2-9 stringi_0.4-1       rvest_0.2.0
## [7] data.table_1.9.4
##
## loaded via a namespace (and not attached):
##  [1] bitops_1.0-6    chron_2.3-45    digest_0.6.8    evaluate_0.5.5
##  [5] formatR_1.0     htmltools_0.2.6 httr_0.6.1      magrittr_1.5
##  [9] markdown_0.7.4  plyr_1.8.1      Rcpp_0.11.4     RCurl_1.95-4.5
## [13] reshape2_1.4.1  rJava_0.9-6     rmarkdown_0.5.1 stringr_0.6.2
## [17] tools_3.1.2     XML_3.98-1.1    XMLRPC_0.3-0    yaml_2.1.13
0 0 votes
Article Rating
Subscribe
Notify of
guest

12 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Guest

윈도우에서도 잘 작동하는거 확인했습니다 🙂
공유 감사합니다//

실행에 있어 몇가지 이슈가 있었는데요,

1. XLConnect library 로딩 -> XLConnect rJava.dll 실행 불가 현상

Error : .onLoad failed in loadNamespace() for ‘rJava’, details:
call: inDL(x, as.logical(local), as.logical(now), …)
error: 공유된 객체 ‘C:/Program Files/R/R-2.15.2/library/rJava/libs/x64/rJava.dll’를 불러올 수 없습니다:
LoadLibrary failure: 지정된 모듈을 찾을 수 없습니다.

-> Sys.setenv(JAVA_HOME=”C:/Program Files/Java/jre8/”)
로 JAVA_HOME을 잡아주어 해결하였습니다.

2. workingDirectory 및에 data 폴더를 만들어줘야하는 문제

-> dir.create(paste(getwd(),”data”,sep=”/”))

3. 파일이름이 한글인데, 깨져서 제대로 저장이 안되는 문제

# f_nm % html_text #파일이름 한글 깨짐…
f_nm f_idx, s_idx를 이용하여 파일이름 부여하는 방식으로 넘어감

등으로 해결을 보았습니다.

#—————————–

#package check & install & load
package.list = c(“rvest”,”httr”,”stringi”,”rJava”,”XLConnect”)

for(i in package.list){
package.path <- find.package(i,quiet=TRUE)
if(length(package.path) == 0){
install.packages(i)
}
}

#JAVA_HOME을 잡아줌(rJava.dll 로딩을 못하는 문제해결)
Sys.setenv(JAVA_HOME="C:/Program Files/Java/jre8/")

#library loading
library(rvest)
library(httr)
library(stringi)
library(XLConnect)
library(data.table)

## 엑셀 데이터 다운로딩 부분

#working dir 아래 data 디렉토리 생성
dir.create(paste(getwd(),"data",sep="/"))
show(paste("output directory path : ",paste(getwd(),"data",sep="/"),sep=""))

su <- file("succ.txt", "w")

agent_nm <- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:35.0) Gecko/20100101 Firefox/35.0"

maxidx %
html_nodes(‘.notiWhite1 .notiBorad01’) %>%
html_text %>% as.numeric %>% max

for(i in maxidx:1){
urls_view <- sprintf("http://rt.molit.go.kr/rtFile.do?cmd=view&seqNo=%d&quot;, i)
r <- GET(urls_view,user_agent(agent_nm))
htxt %
html_text() %>%
stri_trim_both) == ‘첨부파일이 존재하지 않습니다.’) next

download_tags %
html_nodes(‘a[href^=”javascript:jsDown”]’)

for(dtag in download_tags){
dtag %>% html_attr(“href”) %>%
stri_match_all_regex(pattern=”javascript:jsDown\(‘([0-9]+)’,'([0-9]+)’\);”) %>%
.[[1]] %>%
{
f_idx <% as.numeric
s_idx <% as.numeric
}

# f_nm % html_text #파일이름 한글 깨짐…
f_nm <- paste(f_idx, s_idx, ".xls", sep="_") #f_idx, s_idx를 이용하여 파일이름 부여

urls <- sprintf("http://rt.molit.go.kr/rtFile.do?cmd=fileDownload&seq_no=%d&file_seq_no=%d&quot;, f_idx,s_idx)

r <- GET(urls,user_agent(agent_nm))
bin <- content(r, "raw")

#1kb 미만의 데이터는 버림(에러 페이지?)
if(length(bin) < 1000) next
writeBin(bin, sprintf("data/%s",f_nm))
cat(sprintf("%d, %dn", f_idx,s_idx), file = su)
print(sprintf("%d, %d", f_idx,s_idx))
}
}
close(su)

## 엑셀 데이터에서 테이블을 추출해 하나의 아파트 매매 데이터로 통합하는 코드
## 연립 다세대, 단독 다가 데이터도 간단한 코드 변환으로 통합할 수 있다.

f_list %
stri_trans_nfc %>%
.[stri_detect_fixed(.,’매매아파트’)]

total_list <- list()
#파일의 각 시트(지역)를 data.table로 변환하고 필요 필드 추가함
for(xlsf in f_list){
wb <- loadWorkbook(paste0('data/',xlsf))
sells <- list()
fname <- stri_replace_last_fixed(xlsf, '.xls',"")
yyyymm <- substring(fname, 1, 6)
typenm <- substring(fname, 9)

for(nm in getSheets(wb)) {
df <- data.table(readWorksheet(wb, sheet = nm, header = TRUE))
df[,`:=`(region=nm, yyyymm=yyyymm, typenm=typenm)]
df[,`거래금액.만원.`:= stri_replace_all_fixed(`거래금액.만원.`, ',', '')]
sells[[nm]] <- df
}
total_list[[paste0(yyyymm,typenm)]] <- rbindlist(sells)
}

result_sales_dt <- rbindlist(total_list)

#결과 데이터 저장
save(result_sales_dt, file='result_sales_dt.RData')

gogamza

윈도우상에서 동작 여부 확인해 주셔서 감사합니다. ^^

Curycu

윈도우에서도 잘 작동하는거 확인했습니다
공유 감사합니다//

실행에 있어 몇가지 이슈가 있었는데요,

1. XLConnect library 로딩 -> XLConnect rJava.dll 실행 불가 현상

Error : .onLoad failed in loadNamespace() for ‘rJava’, details:
call: inDL(x, as.logical(local), as.logical(now), …)
error: 공유된 객체 ‘C:/Program Files/R/R-2.15.2/library/rJava/libs/x64/rJava.dll’를 불러올 수 없습니다:
LoadLibrary failure: 지정된 모듈을 찾을 수 없습니다.

-> Sys.setenv(JAVA_HOME=”C:/Program Files/Java/jre8/”)
로 JAVA_HOME을 잡아주어 해결하였습니다.

2. workingDirectory 및에 data 폴더를 만들어줘야하는 문제
-> dir.create(paste(getwd(),”data”,sep=”/”))

3. 파일이름이 한글인데, 깨져서 제대로 저장이 안되는 문제

# f_nm % html_text #파일이름 한글 깨짐…
f_nm f_idx, s_idx를 이용하여 파일이름 부여하는 방식으로 넘어감

등으로 해결을 보았습니다.

gogamza

윈도우상에서 동작 여부 확인해 주셔서 감사합니다. ^^

kim_dudals#

# f_nm % html_text
f_nm % html_text(encoding = “UTF-8”)

html_text에서 인코딩 설정을 Unicode로 지정해주면 문제없이 한글로 저정됩니다.
인코딩을 어디서 설정해야 하는지 한참 고민했는데, 의외로 답이 간단했네요.

gogamza

한글 인코딩은 OS 혹은 프로그래밍 언어에 따라서 매우 tricky한 측면이 많아서 일반적으로 맥북이나 리눅스의 UTF-8 로케일 작업을 중심으로 설명하고 있습니다.

여튼 좋은 댓글 감사드립ㄴ다.

Seo Lee

안녕하세요? ^^ 님 덕분에 R도 알게 되고, 따라서 부동산 데이터 분석을 해보려고 합니다.

close(su) 뒤에

Error in file(con, “wb”) : cannot open the connection
In addition: Warning messages:
1: In strsplit(str, “\r\n”) : input string 1 is invalid in this locale
2: In file(con, “wb”) :
cannot open file ‘data/201502쟾썡꽭븘뙆듃.xls’: No such file or directory

라고 나오는데요…혹시 도움을 좀 주실 수 있을런지요?

gogamza

보아하니 윈도우에서 돌리는거 같네요. 제가 윈도 PC를 가지고 있지 않아서 코드를 테스트해볼 순 없습니다.

위 댓글에 보시면 윈도우용 코드를 올려놓으신분이 계십니다. 그 코드를 참고하시길 바랍니다.

[…] 관련 데이터를 가져와야 되는데 크롤링 하는 방법은 필자가 정리해둔 국토교통부 실거래가 데이터 크롤링 코드를 참고해서 수집하면 […]

김진형

좋은 글 잘 봤습니다. 저도 관심이 많아서 제 mac에서 코드를 돌려보려고 하는데,

Error in open.connection(x, “rb”) : cannot open the connection
In addition: Warning message:
‘html’ is deprecated.
Use ‘read_html’ instead.
See help(“Deprecated”)
아래와 같이 에러가 나는데, 국토부 페이지가 바뀌어서 그런건가요??? 어떻게 수정하면 될까요???

김진형

3행 실행할 때 에러가 납니다.
maxidx %
html_nodes(‘.notiWhite1 .notiBorad01’) %>%
html_text %>% as.numeric %>% max

[…] 지난번 포스팅을 통해서 얻은 데이터와 그 이전 포스팅의 아파트 매매가에 미치는 층수, 크기, 년도 효과에 대한 분석의 후속 분석으로 같은 데이터를 기반을 하는 분석이지만 2015년 Q1의 데이터가 어느정도 모였으니 이의 가격동향과 더불어 다중 회귀모형의 비선형적인 효과를 좀더 다른 방식의 알고리즘으로 모델링 해보고 시각화 해보는 과정을 거쳐보도록 하겠다. […]