R 네이버 뉴스(Naver News) Selenium (셀레니움) + 기사 본문
크롤링에 필요한 패키지(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을 살펴봅시다
다이노스 : 네이버 뉴스검색
'다이노스'의 네이버 뉴스검색 결과입니다.
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을 선택하고 ">"를 이용하여 하위 카테고리 갑니다
Sys.sleep(time = 0.5) ##(중요!) 셀레니움은 Sys.sleep을 중간중간 넣어주어야 에러를 줄일수 있습니다 |
일간지 언론사만 필터링 해보겠습니다
F12를 누르고 언론사를 클릭합니다
이를 바탕으로 언론사 중 일간지를 <클릭>하는 코딩을 해봅시다
element <- remDr$findElement("css", "#ca_p1") ## id = ca_p1을 선택
|
"확인" 버튼을 누르고 언론사 필터링을 완료하겠습니다
F12를 누르고 확인을 클릭합니다
이를 바탕으로 확인을 <클릭>하는 코딩을 해봅시다
element <- remDr$findElement("css", "#_nx_option_media > ## id = _nx_option_media을 선택
element$clickElement() ## element를 셀레니움으로 "클릭"(말 그대로 마우스로 클릭하듯이)합니다
|
언론사 필터링한 결과를 최신순으로 정렬하겠습니다
F12로 정보를 확인하고 코딩을 해봅시다
element <- remDr$findElement("css", "div.news_option >
|
## 링크 수집
각 언론사 링크가 아닌 네이버 뉴스에서 서비스하는 링크를 수집하겠습니다
네이버뉴스 서비스로 연결하는 링크를 수집하겠습니다
네이버뉴스 서비스로 뉴스를 보는 링크의 element는 2가지입니다(제가 확인해본 결과는 그렇습니다..)
이를 모두 크롤링하는 코딩입니다
링크_nnews <- c() for(i in 1:10){ ## 검색 결과는 총 8페이지로 구성되어 있습니다만 for문을 10번 돌려 줍니다 if(i==10) break()
|
수집한 링크를 확인합니다
수집한 링크 구조가 다릅니다
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 %>% 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) |
다음 포스팅은 네이버뉴스 댓글을 크롤링 하겠습니다