[R] Web Crawling

1. 웹 크롤링이란?

위키: 웹 크롤러

간단히 말하면 컴퓨터가 자동적으로 웹사이트에 접속해서 필요한 데이터를 수집하는 방법

  • 여러분들이 알고 계시는 구글이나 네이버 같은 검색엔진들이 검색결과를 보여주기 위해 사용하고 있습니다.

2. 웹 크롤링을 왜 배워야 하는가

솔직히 지금 당장 배울 필요는 없습니다…

다만 인터넷에 널려있는 데이터를 수집해서 뭔가를 하고자 할 때, 그 페이지가 한 100페이지쯤 되면 엄청나게 필요합니다.

[주의!!!]

웹크롤러를 실행하면 그렇게 엄청나게 빠르진 않지만.. 사람과 비교하면 엄청나게 빠른 속도로 웹사이트에 접속합니다.

이 말은 단기간에 해당 사이트에 많은 트래픽을 유발시킨다는 건데, 큰 사이트는 보통 사용량 만큼 서버 비용을 부담하지만 작은 사이트 같은 경우는 일일 트래픽 허용량이 정해져 있습니다.

그리고 정확한 것은 아니지만, 한국과 미국은 허용이 되어 있지만 유럽에서는 웹 크롤링은 불법입니다. 가볍게 생각하시면 안됩니다.

왜 이런말을 하냐면.. 여러분들이 어떤 사이트 하나에 많은 양의 페이지를 크롤링 하는 것은 DoS(서비스 거부 공격)와 같다는 말입니다.

물론 여러분들께서 오늘 배우신 걸로 엄청나게 많은 페이지를 크롤링을 한다면, 해당 사이트에서 자동적으로 연결을 끊습니다. 이걸 해결하시려면Sys.sleep이란걸 쓰시면 됩니다.

3. 크롤링을 위한 기초 지식들

  1. URL(페이지 웹 주소)에 대한 기초 지식이 필요합니다.
    • http://www.naver.com 같은 URL은 많이 보셨을 겁니다.
      하지만 https://search.naver.com/search.naver?where=post&sm=tab_pge&query=
      %ED%81%AC%EB%A1%A4%EB%A7%81&st=sim&date_option=0&date_from=&date_to=&
      dup_remove=1&post_blogurl=&post_blogurl_without=&srchby=all&nso=&ie=utf8&start=11

      이런 주소를 보셨는지 모르겠습니다.
    • 두 번째 주소를 보시면 사이트의 구조가 대충 보입니다. 해당 주소는 %ED%81%AC%EB%A1%A4%EB%A7%81 라는 검색 키워드로 네이버에서 블로그 검색을 수행한 결과 입니다. encoding은 UTF-8이고 start=11인 것을 봐선 11번째 검색 결과값부터 브라우저에 출력해주는 URL입니다.
    • 하지만 저희가 이번에 배울 것은 매우 쉬운 단계이므로 그렇게 크게 신경 쓰지 않으셔도 됩니다.
    • 단지 page=이나 start=정도는 눈여겨 보셔야 합니다
  2. HTML구조에 대해서 이해 하셔야 합니다.
    • 크롤링은 결국 인터넷 페이지의 소스를 가져와서 원하는 부분의 태그에 속한 것들을 수집해 오는 것입니다.
    • html 태그는 <body><div><ul></ul></div></body>과 같은 구조가 있습니다.
    • 해당 구조는 body안에 div가 하나 있고 그 안에 ul이 있는건데요… 아시면 나중에 도움이 되겠지만 지금은 브라우저에 있는 개발자 도구를 사용하겠습니다.
    • 크롬 기준 F12키 누르면 [예제 1]과 같은 화면이 브라우져 창에 뜨게 됩니다.
    • 원하는 부분을 클릭 후 우클릭해서 css path를 가져오면 됩니다.
  • 예제 1

예제 1

예제 1

  1. HTTP 요청에 대해서도 조금정도는 아셔야 합니다
    • GET이나 POST, PUT 그리고 DELETE 방식이 있는데 저희가 주로 다루는 것은 GET과 POST이고 그나마도 이번 시간에서는 GET방식의 페이지만 다룹니다.
    • GET과 POST 방식의 가장 큰 차이점은 해당 페이지를 넘어갈 때 URL이 바뀌냐 안 바뀌냐의 차이점입니다.

4. 크롤링 방식의 간단 설명

  1. 패키지를 사용하여 해당 페이지의 소스를 가져온다.
  2. css path나 XML path를 사용하여 원하는 태그에 들어 있는 정보만 저장한다(parsing한다고 합니다).

5. 크롤링에 필요한 패키지들

  1. rvest
    • GET방식 페이지 크롤링에서는 어지간하면 이거 하나로 다 끝납니다.
    • 페이지 소스를 가져오는 것부터 파싱하는 것까지 다 들어있습니다.
  2. httr
    • GET방식이 아닌 페이지일 경우 이 패키지가 필요합니다
    • 하지만 지금 저희는 안 씁니다. 그냥 알아만 두세요
  3. RSelenium
    • … GET이고 POST고 페이지 하나를 가져와야 하는데 트위터나 페이스북을 보시면 스크롤링만 하면 지속적으로 페이지 길이가 늘어나는 걸 볼 수 있습니다.
    • 또는 전부 javascript로 페이지를 만드는 경우도 있습니다.
    • 첫 번째의 경우 ajax를 사용한 비동기화 방식이고 두번짼 javascript로 만들어진 페이진데.. 둘다 파싱하기 힘듭니다.
    • 그럴 때 사용하는 패키지입니다.
  • 저희는 rvest를 사용하여 amazon.com의 페이지를 크롤링 해보겠습니다.

6. 전체소스코드

#install.packages("rvest")
library(rvest)
# Store web url
url <- "http://www.amazon.com/Inside-Blu-ray-Combo-Pack-Digital/product-reviews/B00YCY46VO/ref=cm_cr_pr_btm_link_2?ie=UTF8&showViewpoints=1&sortBy=recent&reviewerType=all_reviews&formatType=all_formats&filterByStar=all_stars&pageNumber="

#declaring variable

stars <- NULL
title <- NULL
author <- NULL
contents <- NULL

#making crawl function
crawl <- function(x){
  for(i in x){
    #merge full URL address
    amz <- read_html(paste0(url,i))
   
    #Scrape 
    #user grade scrape
    starsi <- amz %>%
      #access using CSS PATH
      html_nodes(css = "#cm_cr-review_list > div > div:nth-child(1) > a:nth-child(1) > i > span") %>%
      html_text() %>% substr(1,3)
    #integrating grade data
    stars <- c(stars , starsi )
    
    #title scrape
    titlei <- amz %>% 
      html_nodes(css="#cm_cr-review_list> div > div:nth-child(1) > a.a-size-base.a-link-normal.review-title.a-color-base.a-text-bold") %>% html_text() 
    title <- c(title,titlei)
    
    #review author scrape
    authori <- amz %>% html_nodes(css="#cm_cr-review_list> div  > div:nth-child(2) > span.a-size-base.a-color-secondary.review-byline > a") %>% html_text() 
    author <- c(author,authori)
    
    #review contents scrape
    contentsi <- amz %>% 
      html_nodes(css="#cm_cr-review_list> div  > div:nth-child(4) > span") %>%
      html_text() 
    
    contents <- c(contents,contentsi)
  }
  #integrating whole scrape data
  review <- data.frame(title,author,stars,contents)
  return(review)
}

#crawl the x number of pages.
amz1 <- crawl(1:5)

#make csv file which has whole data.
write.csv(amz1,"amz.csv",row.names = FALSE)

7. 각 스텝 설명

  1. preparing step
    • 크롤링의 기본 구조(Basic Structure of Crawling)
      1. 페이지 소스 추출(Extract Page Source)
        • Library – httr, rvest 외
      2. 파싱(Parsing)
        • Library – rvest 외
    • identifying URL
      • URL이 다른 페이지에 들어갔을때 변한다면 GET방식, 변하지 않는다면 POST 방식
      • 이도저도 안된다면 그냥 RSelenium을 쓰자… 이것마저 안될 수 있다…
      • GET 방식일 때, url구조가 바뀌는것을 확인하는 가장 좋은 방법은, 서로 다른 페이지의 URL을 복사 붙여 넣기하고 바뀐 부분만 찾아보는 것
    • identifying CSS Path
      • Css Path 방식과 Attribute로 접근하는 방식이 있다.
      • 일반적으로 F12키(개발자용 도구)를 누르고 마우스 커서 모양을 눌러 원하는 위치를 클릭하면 소스를 확인 할 수 있다.
      • 마우스 우클릭해서 Copy the Css Selector나 고유선택자 복사를 누르면 그 글에 해당하는 Css Path가 복사 되는데, 전체를 복사하기 위해선 같은 위치에 해당하는 Css Selector를 두개 복사한후 틀린 부분만 지우거나, 상위 단계에서부터 내려오면 된다.
  1. At the first, rvest패키지를 깝니다.
#install.packages("rvest")
library(rvest)
## Loading required package: xml2
  1. Second, 크롤링 하고 싶은 URL을 복사해서 할당하세요
    • Caution! 두페이지 이상 크롤링 하고자 하면 page=이나 start=이 있는 url을 가져오셔야 합니다
    • 일반적으로 해당 url이 페이지를 넘겨주는 역할을 수행합니다.
  • 예시
url <- "http://www.amazon.com/Inside-Blu-ray-Combo-Pack-Digital/product-reviews/B00YCY46VO/ref=cm_cr_pr_btm_link_2?ie=UTF8&showViewpoints=1&sortBy=recent&reviewerType=all_reviews&formatType=all_formats&filterByStar=all_stars&pageNumber="
  1. Third, 변수를 미리 할당해주세요.(2페이지 이상일 때)
    • 원래라면 변수 설정은 필요 없지만, 여러 페이지를 가져올 경우 같은 변수에 데이터를 저장한다면 덮어씌워지기만 합니다.
    • 그렇다고 데이터를 가지고 올때마다 저장할 수도 없고 새로운 변수를 매번 만들수도 없기 때문에, 크게 저장할 변수 하나를 만들고 지속적으로 자기참조해서 저장하기 위해 변수 할당을 미리 해주세요
  • 예시
stars <- NULL
title <- NULL
author <- NULL
contents <- NULL
  1. Fourth, 반복문을 사용합니다.
for(i in x){
  
}
  1. Fifth, 전체 페이지 소스를 가져오세요
    • read_html함수를 사용해서 가져옵니다.
    • read_html을 사용하면 그냥 소스만 가져오는게 아니라 xml구조에 맞춰서 가져오는 거지만.. 그냥 페이지 소스 가져오는 겁니다.
amz <- read_html(paste0(url,as.character(i) ))
  • 예시
amz <- read_html("http://www.amazon.com/Inside-Blu-ray-Combo-Pack-Digital/product-reviews/B00YCY46VO/ref=cm_cr_pr_btm_link_2?ie=UTF8&showViewpoints=1&sortBy=recent&reviewerType=all_reviews&formatType=all_formats&filterByStar=all_stars&pageNumber=1")
print(amz)
## {xml_document}
## <html>
## [1] <head><script><![CDATA[var aPageStart = (new Date()).getTime();]]></ ...
## [2] <body class="a-auix_ux_57388-t1 a-auix_ux_63571-c a-aui_51744-c a-au ...
  • paste0() 함수는 두 문장을 공백없이 하나로 합쳐주는 함수입니다.
a <- "http://www.amazon.com/Inside-Blu-ray-Combo-Pack-Digital/product-reviews/B00YCY46VO/ref=cm_cr_pr_btm_link_2?ie=UTF8&showViewpoints=1&sortBy=recent&reviewerType=all_reviews&formatType=all_formats&filterByStar=all_stars&pageNumber="
b <- 2
c<-paste0(a,as.character(b))
print(c)
## [1] "http://www.amazon.com/Inside-Blu-ray-Combo-Pack-Digital/product-reviews/B00YCY46VO/ref=cm_cr_pr_btm_link_2?ie=UTF8&showViewpoints=1&sortBy=recent&reviewerType=all_reviews&formatType=all_formats&filterByStar=all_stars&pageNumber=2"
  1. Sixth, css path를 사용하여 파싱해서 데이터를 가져옵니다.
starsi <- amz %>%
      #access using CSS PATH
      html_nodes(css = "#cm_cr-review_list > div > div:nth-child(2) > a:nth-child(1) > i > span") %>%
      html_text() %>% substr(1,3)
    #integrating grade data
    stars <- c(stars , starsi )
  • 개발자 도구에서 Copy Css Selector를 사용하면 바로 가져올 수 있습니다.
  • html_nodes 함수로 원하는 태그를 선택한 후, html_text() 을 사용하여 해당 css가 적용된 글자만 가져옵니다.
    • substr은 문장을 글자 하나식 잘라주는 함수인데, 원하는 글자의 시작순서와 끝순서를 정해주면 잘라줍니다.
    • 예를 들면 eg="조교는 바보다" 라는 문장을 substr(eg,5,6)하게 되면 [바보]라는 결과가 나옵니다.
  1. Seventh, 각 변수들을 사용해서 데이터프레임을 만드세요.
    • 함수로 만들었을 경우 return해주세요
  • 예시
review <- data.frame(title,author,stars,contents)

Thank you for listening

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다