R Crawler 3

User-Agent 설정하기

Dr.Kevin 1/26/2018

User-Agent를 추가로 설정해주어야 할까요? 어떤 웹사이트는 HTML RequestUser-Agent(UA)를 확인하고 사람이 요청하는 것이 아니라고 판단되면 정상적으로 응답하지 않습니다. 이런 경우, UA를 추가로 설정해주는 것만으로도 간단하게 해결되는 경우가 있습니다.

크롬의 개발자도구에서 UA를 확인한 후 GET() 함수나 POST() 함수 인자로 추가 설정해주면 됩니다. 또는 UserAgentString.com에서 손쉽게 확인할 수 있습니다.

이번 예제에서는 네이버 부동산에서 제공되는 데이터를 수집하겠습니다. 패키지는 지난 번과 동일하게 불러오면 됩니다.

# 필요 패키지를 불러옵니다.
library(httr)
library(rvest)
library(dplyr)

크롬 개발자도구에서 UA 확인하기

먼저 네이버 부동산으로 접속하고, 개발자도구의 네트워크 탭으로 이동합니다. 하단에 아무런 내용이 없다면 새로고침(F5) 합니다. 그러면 하단에 여러 항목들이 생길 것입니다.

여기에서 가장 위에 위치한 land.naver.com을 클릭합니다. 그럼 오른쪽에 상세 내용이 보이는데 화면을 맨 아래로 이동하여 Request HeadersUser-Agent를 확인합니다. 바로 여기에서 보여지는 값이 이 컴퓨터로 접속할 때 서버로 전달되는 User Agent 입니다. 이 값을 복사하여 httr 패키지의 user_agent() 함수의 인자로 할당합니다.

# User-Agent를 지정합니다.
ua <- user_agent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")

GET 방식으로 웹데이터를 수집하려면 URL을 잘 구성해야 합니다. 이번 예제에서는 서울특별시 영등포구 여의도동으로 구역을 지정하고 아파트 매매 목록을 확인해보겠습니다. 한 화면당 30개의 매물이 출력됩니다. 지금 웹브라우저 주소창에 보이는 URL을 복사한 후 GET() 함수를 이용하여 HTML Request를 해보겠습니다.

# url을 지정합니다.
url <- "http://land.naver.com/article/articleList.nhn?rletTypeCd=A01&tradeTypeCd=A1&hscpTypeCd=A01%3AA03%3AA04&cortarNo=1156011000&articleOrderCode=&siteOrderCode=&cpId=&mapX=126.9310828&mapY=37.5213899&mapLevel=10&minPrc=&maxPrc=&minWrrnt=&maxWrrnt=&minLease=&maxLease=&minSpc=&maxSpc=&subDist=&mviDate=&hsehCnt=&rltrId=&mnex=&mHscpNo=&mPtpRange=&mnexOrder=&location=&ptpNo=&bssYm=&schlCd=&cmplYn="

# html 요청합니다. 
resp <- GET(url)

# 응답 상태코드를 확인합니다.
status_code(resp)
## [1] 403

상태코드가 403으로 출력됩니다. 상태코드가 4xx인 것은 요청 오류를 의미하며 특히 403인가 금지 상태를 나타냅니다. 즉, 서버가 요청을 거부한 것이죠. 자세한 내용은 HTML Response 상태코드에서 확인하시기 바랍니다.

GET() 함수에 ua 인자를 추가한 후 다시 요청해보겠습니다.

# ua 추가해서 html request 합니다.
resp <- GET(url, ua)

# 응답 상태코드를 재확인합니다. 
status_code(resp)
## [1] 200

이제 상태코드가 200으로 정상입니다. 앞에서 말씀드린 바와 같이 User-Agent를 추가해주는 것만으로도 이렇게 간단하게 해결하는 경우도 있습니다. 다행인 것은 관심 있는 데이터가 테이블 형태로 이루어져 있습니다. 따라서 예전에 배웠던 html_table() 함수를 사용하면 간단하게 해결할 수 있습니다.

크롬 개발자도구에 들어가서 해당 테이블 html element를 확인해보니 <table summary="확인매물 리스트" ...>인 것으로 확인되었습니다. 그리고 table tag를 갖는 html element가 딱 한 개입니다. 따라서 html_node() 함수를 사용하겠습니다.

# 매물 리스트를 수집합니다.
aptList <- read_html(resp) %>% 
  html_node(css = "table") %>% 
  html_table(fill = TRUE)

여기서 잠깐! Windows 사용자라면 이 부분에서 에러가 발생될 것입니다. 바로 인코딩 문제 때문인데요. 제가 이 부분을 해결하는 방법을 따로 정리했습니다. 한글 인코딩 문제 해결하기를 먼저 확인하시기 바랍니다.

# 데이터 테이블 구조를 확인합니다.
str(object = aptList)
## 'data.frame':    60 obs. of  9 variables:
##  $ 거래         : chr  "매매" "매매" "매매" "매매" ...
##  $ 종류         : chr  "주상복합" "주상복합" "아파트" "아파트" ...
##  $ 확인일자     : chr  "18.04.04." "18.04.04." "18.04.04." "18.04.04." ...
##  $ 현장확인 사진: chr  "여의도자이\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기" "56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산" "한양\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기" "한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산" ...
##  $ 매물명       : chr  "여의도자이\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기" "56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산" "한양\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기" "한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산" ...
##  $ 면적(㎡)     : chr  "185W/148\n\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t공급면적 185.98㎡\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t"| __truncated__ "56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산" "159B/149\n\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t공급면적 159.71㎡\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t"| __truncated__ "한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산" ...
##  $ 층           : chr  "22/39" "56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산" "9/12" "한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산" ...
##  $ 매물가(만원) : chr  "158,000" "158,000" "150,000" "150,000" ...
##  $ 연락처       : chr  "명가부동산02-783-3333" "56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산" "가이드공인중개사사무소\n\t\t\t\t\t\t\n\t\t\t\t\t\t02-784-4948" "한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산" ...
# 처음 10줄만 확인합니다.
head(x = aptList, n = 10)
##    거래     종류  확인일자
## 1  매매 주상복합 18.04.04.
## 2  매매 주상복합 18.04.04.
## 3  매매   아파트 18.04.04.
## 4  매매   아파트 18.04.04.
## 5  매매   아파트 18.04.04.
## 6  매매   아파트 18.04.04.
## 7  매매   아파트 18.04.04.
## 8  매매   아파트 18.04.04.
## 9  매매   아파트 18.04.04.
## 10 매매   아파트 18.04.04.
##                                                                                                              현장확인 사진
## 1                                      여의도자이\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 2            56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 3                                            한양\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 4  한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산
## 5                                            미성\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 6                            26, 올수리, 이마트3분, 방3, 화1\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 7                                            삼부\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 8                                 삼부28 한강변 재건축대단지\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 9                                            미성\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 10                   26매매 진짜올수리된 입주가능한 추천매물\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
##                                                                                                                     매물명
## 1                                      여의도자이\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 2            56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 3                                            한양\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 4  한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산
## 5                                            미성\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 6                            26, 올수리, 이마트3분, 방3, 화1\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 7                                            삼부\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 8                                 삼부28 한강변 재건축대단지\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 9                                            미성\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t네이버부동산에서 보기
## 10                   26매매 진짜올수리된 입주가능한 추천매물\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
##                                                                                                                                              면적(㎡)
## 1  185W/148\n\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t공급면적 185.98㎡\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t전용면적 148.94㎡
## 2                                       56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 3  159B/149\n\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t공급면적 159.71㎡\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t전용면적 149.52㎡
## 4                             한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산
## 5        98/91\n\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t공급면적 98.64㎡\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t전용면적 91.9㎡
## 6                                                       26, 올수리, 이마트3분, 방3, 화1\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 7       92/77\n\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t공급면적 92.56㎡\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t전용면적 77.69㎡
## 8                                                            삼부28 한강변 재건축대단지\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 9        98/91\n\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t공급면적 98.64㎡\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t전용면적 91.9㎡
## 10                                              26매매 진짜올수리된 입주가능한 추천매물\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
##                                                                                                                         층
## 1                                                                                                                    22/39
## 2            56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 3                                                                                                                     9/12
## 4  한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산
## 5                                                                                                                     4/13
## 6                            26, 올수리, 이마트3분, 방3, 화1\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 7                                                                                                                     9/15
## 8                                 삼부28 한강변 재건축대단지\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 9                                                                                                                     2/13
## 10                   26매매 진짜올수리된 입주가능한 추천매물\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
##    매물가(만원)
## 1       158,000
## 2       158,000
## 3       150,000
## 4       150,000
## 5       110,000
## 6       110,000
## 7       140,000
## 8       140,000
## 9       106,000
## 10      106,000
##                                                                                                                     연락처
## 1                                                                                                    명가부동산02-783-3333
## 2            56 실매물 입주가능 전망좋은세대 올수리 관리잘된\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 3                                                            가이드공인중개사사무소\n\t\t\t\t\t\t\n\t\t\t\t\t\t02-784-4948
## 4  한양50 특A올수리 재건축후 여의도의 중심지구로의 발전예상 \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t매경부동산
## 5                                                                                        골든벨공인중개사사무소02-786-0202
## 6                            26, 올수리, 이마트3분, 방3, 화1\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 7                                                                                                    정화부동산02-782-1800
## 8                                 삼부28 한강변 재건축대단지\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산
## 9                                                                                     롯데캐슬 공인중개사사무소02-783-1002
## 10                   26매매 진짜올수리된 입주가능한 추천매물\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t한경부동산

웹페이지에 출력된 것은 총 30개였는데, 데이터 테이블로 정리된 aptList는 총 60줄입니다. 한 매물당 2줄씩 되어 있습니다. 특이한 것은 html_table() 함수의 인자로 fill = TRUE를 추가해주었기 때문에 원래 NA인 칸에 특정 값이 반복해서 들어가 있는 것을 확인할 수 있습니다.

# 홀수 번째 행만 남기고 짝수 번째 행은 지웁니다. 
# 문자인 행번호를 숫자 벡터로 변환한 후 2로 나누어서 나머지가 1인 행만 남깁니다.
aptList <- aptList[as.numeric(rownames(aptList)) %% 2 == 1, ]

그리고 불필요한 컬럼(현장확인 사진)과 문자열(집주인, \n, \t, 네이버부동산에서 보기)을 삭제하겠습니다.

# 4번째 컬럼을 삭제합니다.
aptList <- aptList[, -4]

# 매물명 컬럼에서 "네이버부동산에서 보기" 지웁니다.
aptList$매물명 <- 
  str_replace_all(string = aptList$매물명, 
                  pattern = "(집주인)*[\n\t]+|네이버부동산에서 보기",
                  replacement = "")

면적(㎡) 컬럼에도 불필요한 문자열(\n, \t, 공급면적, 전용면적)이 있습니다. 모두 지우고 공백을 구분자로 분리하겠습니다. 그리고 문자를 숫자로 변환하기 위해 도 삭제합니다.

# 불필요한 문자열을 삭제합니다.
aptList$`면적(㎡)` <- 
  str_replace_all(string = aptList$`면적(㎡)`, 
                  pattern = "[\n\t]+|공급면적|전용면적|㎡",
                  replacement = "")

# 공백을 구분자로 분리(split)합니다.
str_split(string = aptList$`면적(㎡)`, pattern = " ")
## [[1]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[2]]
## [1] "159B/149" "159.71"   "149.52"  
## 
## [[3]]
## [1] "98/91" "98.64" "91.9" 
## 
## [[4]]
## [1] "92/77" "92.56" "77.69"
## 
## [[5]]
## [1] "98/91" "98.64" "91.9" 
## 
## [[6]]
## [1] "227W/182" "227.82"   "182.45"  
## 
## [[7]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[8]]
## [1] "181/143" "181.76"  "143.61" 
## 
## [[9]]
## [1] "132C/123" "132.39"   "123.27"  
## 
## [[10]]
## [1] "199/156" "199.84"  "156.66" 
## 
## [[11]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[12]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[13]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[14]]
## [1] "207/163" "207.4"   "163.87" 
## 
## [[15]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[16]]
## [1] "156F/125" "156.97"   "125.71"  
## 
## [[17]]
## [1] "156F/125" "156.97"   "125.71"  
## 
## [[18]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[19]]
## [1] "112A/105" "112.53"   "105.72"  
## 
## [[20]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[21]]
## [1] "144B/113" "144.33"   "113.59"  
## 
## [[22]]
## [1] "152/119" "152.07"  "119.21" 
## 
## [[23]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[24]]
## [1] "92/77" "92.56" "77.69"
## 
## [[25]]
## [1] "118/118" "118.12"  "118.12" 
## 
## [[26]]
## [1] "227/179" "227.54"  "179.78" 
## 
## [[27]]
## [1] "81/63" "81.75" "63.83"
## 
## [[28]]
## [1] "152/119" "152.07"  "119.21" 
## 
## [[29]]
## [1] "185W/148" "185.98"   "148.94"  
## 
## [[30]]
## [1] "227W/182" "227.82"   "182.45"

aptList 객체의 면적(㎡) 컬럼 각 행이 길이가 3인 문자 벡터로 바뀌고 전체가 리스트 형태로 출력되었습니다. str_split() 함수는 결과 객체를 리스트 형태로 반환합니다. 이 상태에서 각 행에서 맨 앞에 있는 데이터는 사용하지 않기로 합니다. 대신에 두 번째에 위치한 데이터를 추출하여 공급면적, 세 번째에 위치한 데이터는 전용면적이라는 컬럼명으로 하여 aptList 객체에 추가하겠습니다.

위와 같이 작업을 하려면 sapply() 함수와 [ 함수에 대해서 알아야 합니다. sapply() 함수는 벡터나 리스트를 받아서 같은 함수를 적용해주는 함수입니다. lapply() 함수는 결과 객체를 리스트로 반환해주지만, sapply() 함수는 좀 더 사용자 친화적인 함수로 simplify = TRUE 인자를 추가하면 백터로 반환해줍니다. 그러니 별도로 unlist() 함수를 사용할 필요가 없는 것입니다.

[ 함수는 반복되는 객체들에 대해 같은 위치를 적용시키는 것입니다. 객체 인덱싱할 때 대괄호를 사용한다는 것을 떠올려보시기 바랍니다. 그리고 [ 함수 뒤에 오는 숫자는 참조할 위치를 나타냅니다. 즉, 리스트의 각 원소별로 두 번째에 있는 데이터를 가져오려면 아래와 같이 하면 됩니다.

# 공백을 구분자로 분리한 리스트의 두 번째 데이터만 가져옵니다.
# apply 계열 함수는 주요 인자명이 대문자인 것에 주의합니다! 
sapply(X = str_split(string = aptList$`면적(㎡)`, pattern = " "),
       FUN = "[", 2,
       simplify = TRUE)
##  [1] "185.98" "159.71" "98.64"  "92.56"  "98.64"  "227.82" "185.98"
##  [8] "181.76" "132.39" "199.84" "185.98" "185.98" "185.98" "207.4" 
## [15] "185.98" "156.97" "156.97" "185.98" "112.53" "185.98" "144.33"
## [22] "152.07" "185.98" "92.56"  "118.12" "227.54" "81.75"  "152.07"
## [29] "185.98" "227.82"

이렇게 두 번째와 세 번째에 위치한 데이터를 각각 aptList 객체의 공급면적전용면적 컬럼으로 할당하려면 다음과 같이 합니다.

# 새로운 컬럼들을 생성하고 숫자 벡터로 변환합니다.
aptList$공급면적 <- 
  sapply(X = str_split(string = aptList$`면적(㎡)`, pattern = " "),
         FUN = "[", 2,
         simplify = TRUE) %>% 
  as.numeric()

aptList$전용면적 <- 
  sapply(X = str_split(string = aptList$`면적(㎡)`, pattern = " "),
         FUN = "[", 3,
         simplify = TRUE) %>% 
  as.numeric()

# 면적(㎡) 컬럼을 삭제하고, 첫 6행만 미리보기 합니다.
aptList <- aptList[, -5]
head(x = aptList, n = 10L)
##    거래     종류  확인일자           매물명    층 매물가(만원)
## 1  매매 주상복합 18.04.04.       여의도자이 22/39      158,000
## 3  매매   아파트 18.04.04.             한양  9/12      150,000
## 5  매매   아파트 18.04.04.             미성  4/13      110,000
## 7  매매   아파트 18.04.04.             삼부  9/15      140,000
## 9  매매   아파트 18.04.04.             미성  2/13      106,000
## 11 매매 주상복합 18.04.04.       여의도자이 22/39      180,000
## 13 매매 주상복합 18.04.04.       여의도자이 22/39      160,000
## 15 매매 주상복합 18.04.04. 대우트럼프월드II 22/34      145,000
## 17 매매   아파트 18.04.04.             삼익  2/12      120,000
## 19 매매 주상복합 18.04.04. 롯데캐슬엠파이어 20/39      125,000
##                                                           연락처 공급면적
## 1                                          명가부동산02-783-3333   185.98
## 3  가이드공인중개사사무소\n\t\t\t\t\t\t\n\t\t\t\t\t\t02-784-4948   159.71
## 5                              골든벨공인중개사사무소02-786-0202    98.64
## 7                                          정화부동산02-782-1800    92.56
## 9                           롯데캐슬 공인중개사사무소02-783-1002    98.64
## 11                             GS자이공인중개사사무소02-785-9898   227.82
## 13                             GS자이공인중개사사무소02-785-9898   185.98
## 15                          롯데캐슬 공인중개사사무소02-783-1002   181.76
## 17   모범공인중개사사무소\n\t\t\t\t\t\t\n\t\t\t\t\t\t02-783-1441   132.39
## 19                                         명가부동산02-783-3333   199.84
##    전용면적
## 1    148.94
## 3    149.52
## 5     91.90
## 7     77.69
## 9     91.90
## 11   182.45
## 13   148.94
## 15   143.61
## 17   123.27
## 19   156.66

이번에는 연락처 컬럼을 정리해보겠습니다. 부동산과 전화번호가 복잡하게 붙어 있습니다. 먼저 불필요한 문자열(\n, \t, 부동산, 공인중개사사무소)을 지워보겠습니다.

# 불필요한 문자열을 삭제합니다.
aptList$연락처 <- 
  str_replace_all(string = aptList$연락처, 
                  pattern = "[\n\t]+|부동산|공인중개사(사무소)*",
                  replacement = "")

이제 부동산 상호명과 전화번호를 분리하겠습니다. 부동산 상호명보다는 전화번호가 좀 더 규칙을 갖고 있으니 전화번호를 기준으로 분리하는 편이 낫습니다. 전화번호는 (예컨데 0505-1234-5678과 같은 번호도 있으니) 4자리가 숫자가 세번 반복되고 그 사이에 -이 있는 형태로 지정하면 될 것입니다. 문자열을 추출하는 함수는 str_extract_all()을 사용하면 됩니다.

# 연락처 컬럼에서 전화번호만 추출하여 전화번호 컬럼을 새로 만듭니다.
# str_extract_all()은 리스트로 결과를 반환하므로 unlist() 추가합니다.!
aptList$전화번호 <- 
  str_extract_all(string = aptList$연락처,
                  pattern = "\\d{1,4}-\\d{1,4}-\\d{1,4}") %>% 
  unlist()

# 연락처 컬럼에서 전화번호에 해당하는 부분 삭제합니다.
# 일부 부동산 상호명과 전화번호 사이에 공백이 있으므로 str_trim() 추가합니다!
aptList$연락처 <- 
  str_replace_all(string = aptList$연락처, 
                  pattern = "\\d{1,4}-\\d{1,4}-\\d{1,4}",
                  replacement = "") %>% 
  str_trim()

마지막으로 매물가(만원) 컬럼에서 comma(,)를 삭제하고 문자를 숫자 벡터로 변환하겠습니다. 아울러 컬럼명도 호가로 변경합니다. 동시에 연락처부동산으로 함께 바꾸겠습니다.

# 매물가(만원) 컬럼에서 콤마(,)를 없애고 숫자 벡터로 변환합니다.
aptList$`매물가(만원)` <- 
  str_replace_all(string = aptList$`매물가(만원)`, 
                  pattern = ",",
                  replacement = "") %>% 
  as.numeric()

# 컬럼명을 바꿉니다.
colnames(aptList)[6:7] <- c("호가", "부동산")

앗! 컬럼이 뒤죽박죽 되어 있군요. 보기 좋게(?) 순서를 바꿔보도록 하겠습니다.

# 컬럼 순서를 변경합니다. (아래 2가지 방법 중 하나를 선택합니다.)

# 1번 : 컬럼 위치를 지정하는 방법으로 상대적으로 간결합니다.
aptList <- aptList[, c(1:4, 8:9, 5:7, 10)]

# 2번 : 컬럼명을 원하는 순서대로 기입할 수도 있습니다. 
aptList <- aptList[, c("거래", "종류", "확인일자", "매물명", "공급면적", 
                       "전용면적", "층", "호가", "부동산", "전화번호")]

# 처음 10행만 미리보기 합니다.
head(x = aptList, n = 10L)
##    거래     종류  확인일자           매물명 공급면적 전용면적    층   호가
## 1  매매 주상복합 18.04.04.       여의도자이   185.98   148.94 22/39 158000
## 3  매매   아파트 18.04.04.             한양   159.71   149.52  9/12 150000
## 5  매매   아파트 18.04.04.             미성    98.64    91.90  4/13 110000
## 7  매매   아파트 18.04.04.             삼부    92.56    77.69  9/15 140000
## 9  매매   아파트 18.04.04.             미성    98.64    91.90  2/13 106000
## 11 매매 주상복합 18.04.04.       여의도자이   227.82   182.45 22/39 180000
## 13 매매 주상복합 18.04.04.       여의도자이   185.98   148.94 22/39 160000
## 15 매매 주상복합 18.04.04. 대우트럼프월드II   181.76   143.61 22/34 145000
## 17 매매   아파트 18.04.04.             삼익   132.39   123.27  2/12 120000
## 19 매매 주상복합 18.04.04. 롯데캐슬엠파이어   199.84   156.66 20/39 125000
##      부동산    전화번호
## 1      명가 02-783-3333
## 3    가이드 02-784-4948
## 5    골든벨 02-786-0202
## 7      정화 02-782-1800
## 9  롯데캐슬 02-783-1002
## 11   GS자이 02-785-9898
## 13   GS자이 02-785-9898
## 15 롯데캐슬 02-783-1002
## 17     모범 02-783-1441
## 19     명가 02-783-3333

이상으로 웹사이트로부터 데이터를 수집하고 stringr 패키지의 몇 가지 주요 함수와 정규표현식 일부를 활용하여 텍스트 데이터를 정리하는 과정을 소개해 드렸습니다. 웹크롤러를 만들어 데이터를 수집하고 데이터 분석을 위한 데이터 정제작업(영어로는 data wrangling)까지 상당한 시간과 노력이 소요됩니다. 따라서 이런 작업에 익숙해지도록 반복하여 훈련하시기 바랍니다.

다음에는 자바스크립트로 인해 원하는 HTML을 한 번에 가져오지 못하는 경우, 이를 해결하는 방법에 대해 소개하겠습니다.

Written on January 26, 2018