R + Crawling (크롤링)

R 네이버 뉴스(Naver News) Selenium (셀레니움) + 기사 본문

구렁이 알(Python/R) 2020. 5. 7. 21:26

출처 : www.dinos.com

크롤링에 필요한 패키지(package)와 라이브러리(library)는 아래와 같습니다

 

install.packages(c("dplyr", "httr", "jsonlite", "rJava", "RSelenium", "stringr")

  • library(dplyr)
  • library(httr)
  • library(jsonlite)
  • library(rJava)
  • library(RSelenium)
  • library(stringr)

 

셀레니움(Selenium)을 실행해봅시다

https://r-pyomega.tistory.com/7?category=873554

 

R 크롤링 RSelenium (셀레니움) 을 크롬에서 구동하기

R에서 Selenium을 구동하려면 Java를 설치해야 합니다. Java설치는 https://r-pyomega.tistory.com/6 를 참고해주시길 바랍니다 Java설치 이후에 C드라이브에 r-selenium폴더를 만들어 아래 3파일을 다운 받습니다..

r-pyomega.tistory.com

 

 

#### 네이버뉴스 검색 페이지

네이버 포탈에서 <다이노스>를 검색하고

기간 옵션을 설정하고

최신순으로 정렬한 페이지입니다

 

 

## URL 주소

URL을 살펴봅시다

https://search.naver.com/search.naver?where=news&query=%EB%8B%A4%EC%9D%B4%EB%85%B8%EC%8A%A4&sm=tab_srt&sort=1&photo=0&field=0&reporter_article=&pd=3&ds=2020.01.01&de=2020.03.31&docid=&nso=so%3Add%2Cp%3Afrom20200101to20200331%2Ca%3Aall&mynews=0&refresh_start=0&related=0 

 

다이노스 : 네이버 뉴스검색

'다이노스'의 네이버 뉴스검색 결과입니다.

search.naver.com

 

복잡한 URL를 분리해봅시다

main <- "https://search.naver.com/search.naver?&where=news&query="
검색어 <- "다이노스"                  ## 검색어입니다

mid1 <- "&sm=tab_srt&sort=1&photo=0&field=0&reporter_article=&pd=3&ds="
시작일1 <- "2020.01.01"              ## 설정 기간 시작일입니다
부터1 <- "&de="
종료일1 <- "2020.03.31"              ## 설정 기간 종료일입니다

mid2 <- "&docid=&nso=so%3Ar%2Cp%3Afrom"
시작일2 <- "20200101"               ## 설정 기간 시작일입니다
부터2 <- "to"
종료일2 <- "20200331"               ## 설정 기간 종료일입니다

mid4 <- "%2Ca%3Aall&mynews=0&refresh_start=0&related=0"

 

 

## URL 이어붙이기

target <- paste(main, 검색어, sep = "")

target <- paste(target, mid1, sep = "")
target <- paste(target, 시작일1, sep = "")
target <- paste(target, 부터1, sep = "")
target <- paste(target, 종료일1, sep = "")

target <- paste(target, mid2, sep = "")
target <- paste(target, 시작일2, sep = "")
target <- paste(target, 부터2, sep = "")
target <- paste(target, 종료일2, sep = "")

target <- paste(target,mid4, sep = "")

remDr$navigate(target)   ## 네이버 뉴스 검색 결과 페이지로 이동합니다

 

 

 

※ 번외

포털사이트는 대부분의 언론사의 뉴스를 제공하여 이를 이용자가 모두 볼 수 있다는 장점도 있지만, 기사의 내용이 복붙형태로 재생산되면서 사실상 똑같은 기사가 범람하는 단점이 있습니다

GET / POST 형식에서는 언론사 필터링하는 방법을 못찾았습니다만... 셀레니움으로는 가능합니다

필요하신분은 참고하십시요

이와같은 방법으로 크롤링에 마우스 클릭이 필요할때 응용하실수 있습니다

 

 

 

## 언론사 선택 + 최신순 정렬

 

F12를 누르고 언론사를 클릭합니다

이를 바탕으로 언론사를 <클릭>하는 코딩을 해봅시다

element <- remDr$findElement("css", "#news_popup >   ## id = news_popup을 선택하고 ">"를 이용하여 하위 카테고리                                                                                  갑니다
                                                  a:nth-child(1)")     ## 1번째 "a"를 가져옵니다


element$clickElement()                                   ## element를 셀레니움으로 "클릭"(말 그대로 마우스로 클릭하듯이)합니다

 

Sys.sleep(time = 0.5)                              ##(중요!) 셀레니움은 Sys.sleep을 중간중간 넣어주어야 에러를 줄일수 있습니다

 

 

일간지 언론사만 필터링 해보겠습니다

F12를 누르고 언론사를 클릭합니다

이를 바탕으로 언론사 중 일간지를 <클릭>하는 코딩을 해봅시다

element <- remDr$findElement("css", "#ca_p1")    ## id = ca_p1을 선택


element$clickElement()                                   ## element를 셀레니움으로 "클릭"(말 그대로 마우스로 클릭하듯이)합니다


Sys.sleep(time = 0.5)                              ##(중요!) 셀레니움은 Sys.sleep을 중간중간 넣어주어야 에러를 줄일수 있습니다

 

 

"확인" 버튼을 누르고 언론사 필터링을 완료하겠습니다

F12를 누르고 확인을 클릭합니다

이를 바탕으로 확인을 <클릭>하는 코딩을 해봅시다

element <- remDr$findElement("css", "#_nx_option_media >                          ## id = _nx_option_media을 선택        
                                     div.con_bx >
                                     div.view_btn > 
                                     button.impact._submit_btn")

 

element$clickElement()                                   ## element를 셀레니움으로 "클릭"(말 그대로 마우스로 클릭하듯이)합니다


Sys.sleep(time = 0.5)                              ##(중요!) 셀레니움은 Sys.sleep을 중간중간 넣어주어야 에러를 줄일수 있습니다

 

 

언론사 필터링한 결과를 최신순으로 정렬하겠습니다

F12로 정보를 확인하고 코딩을 해봅시다

element <- remDr$findElement("css", "div.news_option > 
                                     ul.sort > 
                                     li:nth-child(2)")               ## 2번째 "li"를 가져옵니다


element$clickElement()                                  ## element를 셀레니움으로 "클릭"(말 그대로 마우스로 클릭하듯이)합니다


Sys.sleep(time = 0.5)                             ##(중요!) 셀레니움은 Sys.sleep을 중간중간 넣어주어야 에러를 줄일수 있습니다

 

 

## 링크 수집

각 언론사 링크가 아닌 네이버 뉴스에서 서비스하는 링크를 수집하겠습니다

네이버뉴스 서비스로 연결하는 링크를 수집하겠습니다

네이버뉴스 서비스로 뉴스를 보는 링크의 element는 2가지입니다(제가 확인해본 결과는 그렇습니다..)

이를 모두 크롤링하는 코딩입니다

링크_nnews <- c()

for(i in 1:10){                                    ## 검색 결과는 총 8페이지로 구성되어 있습니다만 for문을 10번 돌려 줍니다
  tryCatch({

    cat('현재', i, '페이지 수집 중! \n') 
    
    frontpage <- remDr$getPageSource()[[1]]
    body <- frontpage %>% read_html() 
    

    ##링크
    
    링크.tmp1 <- body %>% 
      html_nodes("dd.txt_inline") %>%
      html_nodes("a._sp_each_url") %>%
      html_attr("href")
    
    링크.tmp2 <- body %>% 
      html_nodes("span.txt_sinfo") %>%
      html_nodes("a._sp_each_url") %>%
      html_attr("href")
    
    링크.tmp <- append(링크.tmp1,링크.tmp2)
    링크.tmp <- 링크.tmp %>% unique()
    
    링크_nnews <- append(링크_nnews,링크.tmp)
    

if(i==10) break()
    
    element <- remDr$findElement("css", "#main_pack >     
                             div.news.mynews.section._prs_nws>
                             div.paging >
                             a.next")
    element$clickElement()                          ## "다음페이지"를 클릭하는 명령어입니다


    Sys.sleep(time = 0.5)
    
  }, error = function(e) cat("불러올 수 없습니다!\n"))
  
}


링크_nnews <- 링크_nnews %>% unique()    ## 혹시라도 모를 중복 결과를 제거하기 위해 실행합니다

 

수집한 링크를 확인합니다

수집한 링크 구조가 다릅니다

1, 15, 29번째 링크는 https://news.naver.로 시작합니다 

나머지 링크는 http://sports.news.naver.로 시작합니다

 

링크도 다른만큼 해당 링크의 페이지 구조도 다릅니다

각각에 대해 다른 코드를 짜야합니다

 

 

 

 

# https://news.naver. case 

 

우선 https://news.naver.로 시작하는 링크부터 크롤링을 하겠습니다

전체 링크에서 https://news.naver.가 포함된 링크만 뽑아냅니다

news.naver <- grep("https://news.naver",링크_nnews)
링크_news.naver <- 링크_nnews[news.naver]

링크_news.naver %>% head()

첫번째 링크를 타고 들어가 크롤링할 element를 확인합시다

remDr$navigate(링크_news.naver[1])

 

이 문서에서 크롤링 할 element(빨간색 박스)와 뉴스 링크를 저장할 벡터공간을 만듭니다

언론사_news.naver <- c()

날짜_news.naver <- c()
제목_news.naver <- c()
본문_news.naver <- c()

주소_news.naver <- c()

좋아_news.naver <- c()
슬퍼_news.naver <- c()
화나_news.naver <- c()
팬이_news.naver <- c()
후속_news.naver <- c()

 

벡터 공간에 element를 넣을 명령어를 작성합니다


for (i in 1:length(링크_news.naver)){
  
  tryCatch({
    remDr$navigate(링크_news.naver[i])
    body <- remDr$getPageSource()[[1]]
    
    cat('현재', i, '페이지 수집 중! \n') 
    
    body <- body %>% read_html()
    
    
    ##언론사(최종수정기사입력)
    
    언론사.tmp2 <- body %>% 
      html_nodes("div.link_news") %>% 
      html_nodes("h3") %>% 
      html_text()  
    
    언론사.tmp2 <- str_sub(언론사.tmp2,end=5)
    언론사.tmp2 <- 언론사.tmp2 %>% str_trim()
    
    if (length(언론사.tmp2) != 0) {
      언론사_news.naver <- append(언론사_news.naver, 언론사.tmp2)
    } else {
      언론사_news.naver <- append(언론사_news.naver, "수동확인")
    }
    
    
    ##날짜(최종수정기사입력)
    
    날짜.tmp2 <- body %>% 
      html_nodes("span.t11") %>% 
      html_text()  
    
    날짜.tmp2 <-날짜.tmp2[2]
    
    if (length(날짜.tmp1) != 0) {
      날짜_news.naver <- append(날짜_news.naver, 날짜.tmp1)
    } else {
      날짜_news.naver <- append(날짜_news.naver, "수동확인")
    }
    
    
    
    ##제목
    
    제목.tmp2 <- body %>% 
      html_nodes("h3.tts_head") %>% 
      html_text()
    
    if (length(제목.tmp2) != 0) {
      제목_news.naver <- append(제목_news.naver, 제목.tmp2)
    } else {
      제목_news.naver <- append(제목_news.naver, "수동확인")
    }
    
    
    ##본문
    
    본문.tmp2 <- body %>% 
      html_nodes("div#articleBodyContents") %>% 
      html_text()
    
    if (length(본문.tmp2) != 0) {
      본문_news.naver <- append(본문_news.naver, 본문.tmp2)
    } else {
      본문_news.naver <- append(본문_news.naver, "수동확인")
    }
    
    
    
    ## 반응
    
    반응.tmp1 <- body %>% 
      html_nodes("ul.u_likeit_layer") %>% 
      html_text()
    
    반응.tmp1 <- 반응.tmp1[1]
    
    반응.tmp1 <- str_replace_all(반응.tmp1,"\t","")
    반응.tmp1 <- strsplit(반응.tmp1, split="\n")
    
    
    #좋아
    
    좋아.tmp11 <- 반응.tmp1[[1]][3]
    좋아.tmp12 <- 반응.tmp1[[1]][4]
    
    좋아.tmp1 <- paste0(좋아.tmp11,":",좋아.tmp12)
    
    if (length(좋아.tmp1) != 0) {
      좋아_news.naver <- append(좋아_news.naver, 좋아.tmp1)
    } else {
      좋아_news.naver <- append(좋아_news.naver, "수동확인")
    }
    
    
    #슬퍼
    
    슬퍼.tmp11 <- 반응.tmp1[[1]][9]
    슬퍼.tmp12 <- 반응.tmp1[[1]][10]
    
    슬퍼.tmp1 <- paste0(슬퍼.tmp11,":",슬퍼.tmp12)
    
    if (length(슬퍼.tmp1) != 0) {
      슬퍼_news.naver <- append(슬퍼_news.naver, 슬퍼.tmp1)
    } else {
      슬퍼_news.naver <- append(슬퍼_news.naver, "수동확인")
    }
    
    
    #화나
    
    화나.tmp11 <- 반응.tmp1[[1]][15]
    화나.tmp12 <- 반응.tmp1[[1]][16]
    
    화나.tmp1 <- paste0(화나.tmp11,":",화나.tmp12)
    
    if (length(화나.tmp1) != 0) {
      화나_news.naver <- append(화나_news.naver, 화나.tmp1)
    } else {
      화나_news.naver <- append(화나_news.naver, "수동확인")
    }
    
    
    #팬이
    
    팬이.tmp11 <- 반응.tmp1[[1]][21]
    팬이.tmp12 <- 반응.tmp1[[1]][22]
    
    팬이.tmp1 <- paste0(팬이.tmp11,":",팬이.tmp12)
    
    if (length(팬이.tmp1) != 0) {
      팬이_news.naver <- append(팬이_news.naver, 팬이.tmp1)
    } else {
      팬이_news.naver <- append(팬이_news.naver, "수동확인")
    }
    
    
    #후속
    
    후속.tmp11 <- 반응.tmp1[[1]][27]
    후속.tmp12 <- 반응.tmp1[[1]][28]
    
    후속.tmp1 <- paste0(후속.tmp11,":",후속.tmp12)
    
    if (length(후속.tmp1) != 0) {
      후속_news.naver <- append(후속_news.naver, 후속.tmp1)
    } else {
      후속_news.naver <- append(후속_news.naver, "수동확인")
    }
    
    
    
    ##주소
    
    주소_news.naver <- append(주소_news.naver , 링크_news.naver[i])
    
    Sys.sleep(time = 1)
    
    
  }, error = function(e) cat("불러올 수 없습니다!\n"))
}

 

수집한 element의 길이를 확인합니다

length(날짜_news.naver)
length(제목_news.naver)
length(본문_news.naver)
length(주소_news.naver)

length(좋아_news.naver)
length(슬퍼_news.naver)
length(화나_news.naver)
length(팬이_news.naver)
length(후속_news.naver)

 

수집한 element 모두 길이가 같습니다

 

 

 

# https://sports.news case

https://sports.news로 시작하는 링크도 https://news.naver.와 흐름은 같습니다

먼저 http://sports.news포함된 링크만 뽑아냅니다

sports.news <- grep("http://sports.news",링크_nnews)
링크_sports.news <- 링크_nnews[sports.news]

링크_sports.news %>% head()

 

해당링크로 찾아가 크롤링 할 element와 뉴스 링크를 저장할 벡터공간을 만듭니다

언론사_sports.news <- c()

날짜_sports.news <- c()
제목_sports.news <- c()
본문_sports.news <- c()
주소_sports.news <- c()

좋아_sports.news <- c()
슬퍼_sports.news <- c()
화나_sports.news <- c()
팬이_sports.news <- c()
후속_sports.news <- c()

 

벡터 공간에 element를 넣을 명령어를 작성합니다


for (i in 1:length(링크_sports.news)){
  
  tryCatch({
    remDr$navigate(링크_sports.news[i])
    body <- remDr$getPageSource()[[1]]
    
    cat('현재', i, '페이지 수집 중! \n') 
    
    body <- body %>% read_html()
    
    
    ##언론사(최종수정기사입력)
    
    언론사.tmp1 <- body %>% 
      html_nodes("div.link_popular_news") %>% 
      html_nodes("span.logo") %>% 
      html_text()  
    
    if (length(언론사.tmp1) != 0) {
      언론사_sports.news <- append(언론사_sports.news, 언론사.tmp1)
    } else {
      언론사_sports.news <- append(언론사_sports.news, "수동확인")
    }
    
    
    ##날짜(최종수정기사입력)
    
    날짜.tmp1 <- body %>% 
      html_nodes("div.info") %>% 
      html_nodes("span") %>% 
      html_text()  
    
    날짜.tmp1 <- 날짜.tmp1[2]
    
    if (length(날짜.tmp1) != 0) {
      날짜_sports.news <- append(날짜_sports.news, 날짜.tmp1)
    } else {
      날짜_sports.news <- append(날짜_sports.news, "수동확인")
    }
    
    
    ##제목
    
    제목.tmp1 <- body %>% 
      html_nodes("h4.title") %>% 
      html_text()
    
    if (length(제목.tmp1) != 0) {
      제목_sports.news <- append(제목_sports.news, 제목.tmp1)
    } else {
      제목_sports.news <- append(제목_sports.news, "수동확인")
    }
    
    
    ##본문
    
    본문.tmp1 <- body %>% 
      html_nodes("div#newsEndContents") %>% 
      html_text()
    
    if (length(본문.tmp1) != 0) {
      본문_sports.news <- append(본문_sports.news, 본문.tmp1)
    } else {
      본문_sports.news <- append(본문_sports.news, "수동확인")
    }
    
    
    ## 반응
    
    반응.tmp1 <- body %>% 
      html_nodes("ul.u_likeit_inline") %>% 
      html_text()
    
    반응.tmp1 <- str_replace_all(반응.tmp1,"\t","")
    반응.tmp1 <- strsplit(반응.tmp1, split="\n")
    
    
    #좋아
    
    좋아.tmp11 <- 반응.tmp1[[1]][3]
    좋아.tmp12 <- 반응.tmp1[[1]][4]
    
    좋아.tmp1 <- paste0(좋아.tmp11,":",좋아.tmp12)
    
    if (length(좋아.tmp1) != 0) {
      좋아_sports.news <- append(좋아_sports.news, 좋아.tmp1)
    } else {
      좋아_sports.news <- append(좋아_sports.news, "수동확인")
    }
    
    
    #슬퍼
    
    슬퍼.tmp11 <- 반응.tmp1[[1]][9]
    슬퍼.tmp12 <- 반응.tmp1[[1]][10]
    
    슬퍼.tmp1 <- paste0(슬퍼.tmp11,":",슬퍼.tmp12)
    
    if (length(슬퍼.tmp1) != 0) {
      슬퍼_sports.news <- append(슬퍼_sports.news, 슬퍼.tmp1)
    } else {
      슬퍼_sports.news <- append(슬퍼_sports.news, "수동확인")
    }
    
    
    #화나
    
    화나.tmp11 <- 반응.tmp1[[1]][15]
    화나.tmp12 <- 반응.tmp1[[1]][16]
    
    화나.tmp1 <- paste0(화나.tmp11,":",화나.tmp12)
    
    if (length(화나.tmp1) != 0) {
      화나_sports.news <- append(화나_sports.news, 화나.tmp1)
    } else {
      화나_sports.news <- append(화나_sports.news, "수동확인")
    }
    
    
    #팬이
    
    팬이.tmp11 <- 반응.tmp1[[1]][21]
    팬이.tmp12 <- 반응.tmp1[[1]][22]
    
    팬이.tmp1 <- paste0(팬이.tmp11,":",팬이.tmp12)
    
    if (length(팬이.tmp1) != 0) {
      팬이_sports.news <- append(팬이_sports.news, 팬이.tmp1)
    } else {
      팬이_sports.news <- append(팬이_sports.news, "수동확인")
    }
    
    
    #후속
    
    후속.tmp11 <- 반응.tmp1[[1]][27]
    후속.tmp12 <- 반응.tmp1[[1]][28]
    
    후속.tmp1 <- paste0(후속.tmp11,":",후속.tmp12)
    
    if (length(후속.tmp1) != 0) {
      후속_sports.news <- append(후속_sports.news, 후속.tmp1)
    } else {
      후속_sports.news <- append(후속_sports.news, "수동확인")
    }
    
    
    
    ##주소
    
    주소_sports.news <- append(주소_sports.news , 링크_sports.news[i])
    
    Sys.sleep(time = 1)
    
    
  }, error = function(e) cat("불러올 수 없습니다!\n"))
}

 

수집한 element의 길이를 확인합니다

length(언론사_sports.news)

length(날짜_sports.news)
length(제목_sports.news)
length(본문_sports.news)
length(주소_sports.news)

length(좋아_sports.news)
length(슬퍼_sports.news)
length(화나_sports.news)
length(팬이_sports.news)
length(후속_sports.news)

 

수집한 element 길이가 같습니다

 

## 데이터 전처리

데이터 전처리를 합니다

기사본문에 필요없는 데이터를 지우도록 합니다


본문_sports.news <- str_replace_all(본문_sports.news,"\n", "")
본문_sports.news <- str_replace_all(본문_sports.news,"\t", "")
본문_sports.news <- str_replace_all(본문_sports.news,"//", "")
본문_sports.news <- str_replace_all(본문_sports.news,"flash 오류를 우회하기 위한 함수 추가", "")
본문_sports.news <- str_replace_all(본문_sports.news,"function _flash_removeCallback()", "")
본문_sports.news <- str_replace_all(본문_sports.news,"\\()", "")
본문_sports.news <- str_replace_all(본문_sports.news,"\\{}", "")


본문_news.naver <- str_replace_all(본문_news.naver,"\n", "")
본문_news.naver <- str_replace_all(본문_news.naver,"\t", "")
본문_news.naver <- str_replace_all(본문_news.naver,"//", "")
본문_news.naver <- str_replace_all(본문_news.naver,"flash 오류를 우회하기 위한 함수 추가", "")
본문_news.naver <- str_replace_all(본문_news.naver,"function _flash_removeCallback()", "")
본문_news.naver <- str_replace_all(본문_news.naver,"\\()", "")
본문_news.naver <- str_replace_all(본문_news.naver,"\\{}", "")

 

## 하나의 데이터프레임으로 합침

https://news.naver. case와 https://sports.news case 각각을 데이터프레임으로 만들고,

변수이름을 변경한 후,

두 데이터 프레임을 합치겠습니다

paper_sports.news <- data.frame(날짜_sports.news, 제목_sports.news, 본문_sports.news, 주소_sports.news, 좋아_sports.news, 슬퍼_sports.news, 화나_sports.news, 팬이_sports.news, 후속_sports.news)

paper_news.naver <- data.frame(날짜_news.naver, 제목_news.naver, 본문_news.naver, 주소_news.naver, 좋아_news.naver, 슬퍼_news.naver, 화나_news.naver, 팬이_news.naver, 후속_news.naver)


paper_sports.news <- rename(paper_sports.news, c("날짜" = 날짜_sports.news,
                                                                   "제목" = 제목_sports.news,
                                                                   "본문" = 본문_sports.news, 
                                                                   "주소" = 주소_sports.news,
                                                                   "좋아요" = 좋아_sports.news, 
                                                                   "슬퍼요" = 슬퍼_sports.news,
                                                                   "화나요" = 화나_sports.news, 
                                                                   "팬이에요" = 팬이_sports.news,
                                                                   "후속기사" = 후속_sports.news))

paper_news.naver <- rename(paper_news.naver, c("날짜" = 날짜_news.naver, 
                                                                  "제목" = 제목_news.naver,
                                                                  "본문" = 본문_news.naver, 
                                                                  "주소" = 주소_news.naver,
                                                                  "좋아요" = 좋아_news.naver, 
                                                                  "슬퍼요" = 슬퍼_news.naver,
                                                                  "화나요" = 화나_news.naver, 
                                                                  "팬이에요" = 팬이_news.naver,
                                                                  "후속기사" = 후속_news.naver))

paper_nnews <- rbind(paper_sports.news,paper_news.naver)

csv로 저장하여 결과를 확인합니다

www.write.csv(paper_nnews, file = "D:/paper_nnews_다이노스.csv", row.names=FALSE)

 

다음 포스팅은 네이버뉴스 댓글을 크롤링 하겠습니다