[재능상품] R을 이용한 과거 네이버/zum/구글 실시간 검색어 수집 후 코로나 관련 키워드 추출

 정보

  • 업무명     : R을 이용한 과거 네이버/zum/구글 실시간 검색어 수집 후 코로나 관련 키워드 추출

  • 작성자     : 박진만

  • 작성일     : 2020-11-30

  • 설   명      :

  • 수정이력 :

 

 내용

[개요]

  • 안녕하세요? 웹 개발 및 연구 개발을 담당하고 있는 해솔입니다.

  • 다년간 축적된 경험 (기상학 학술 보고서 및 국/영문 학술 논문 게재, 블로그 운영, IT 회사 웹 개발 담당) 및 노하우를 바탕으로 개개인에게 맞춤형 솔루션을 수행할 수 있습니다.

  • 특히 재능 플랫폼 (크몽, 오투잡, 해피캠퍼스, 레포트 월드)에서 누구보다도 경쟁력 있는 가격으로 양질의 서비스를 제공하고 있습니다.

    • 아스키 형식의 텍스트 (text) 파일부터 과학자료 형식 (HDF, H5, NetCDF, Grib, Grb) 및 Data Base (DB) 자료까지 다양한 형태의 자료를 이용하여 수집, 전처리, 분석, 시각화해 드립니다.

    • 또한 웹 사이트에 대한 정보를 이용한 웹 크롤링 및 그에 따른 엑셀 및 DB 구축도 가능합니다.

    • 아울러 기초 통계 (빈도분포, Prired t-test, Wilcoxn 등)에서 지도/비지도 학습을 통한 회귀모형 구축에 이르기 까지 효율적인 통계 정보를 제공합니다.

    • 최근 대한민국의 후속위성인 천리안위성 2A호 웹 서비스 서브시스템 및 환경위성 2B호 통합 자료처리 서브시스템에 대한 웹 개발을 수행하였습니다.

  • 그리고 해솔 블로그에서는 다양한 기상학/천문학 정보와 더불어 사무 자동화/프로그래밍 언어를 소개하오니 방문 부탁드립니다.

  • 좋은 하루 보내세요.

 

[재능플랫폼] 오투잡

 

[IT개발 - 응용프로그래밍] 통계 분석, 데이터 분석, 시각화를 성실하게 해 드립니다. - 재능마켓 �

판매가격:10,000원, [소개] - 데이터산업진흥원 데이터 가공 공급기업 선정 - 정보통신산업 진흥원 데이터 가공 공급기업 선정 - 다년간 축적된 경험 노하우를 바탕으로 개개인에게 맞춤형 솔루션�

www.otwojob.com

 

[재능플랫폼] 크몽

 

데이터수집, 파싱, 크롤링 해 드립니다. | 50,000원부터 시작 가능한 총 평점 0점의 IT·프로그래밍,

0개 총 작업 개수 완료한 총 평점 0점인 shlee1990의 IT·프로그래밍, 데이터분석·리포트, 데이터 마이닝·크롤링 서비스를 0개의 리뷰와 함께 확인해 보세요. IT·프로그래밍, 데이터분석·리포트, 데

kmong.com

 

 요청

[세부 사항]

  • R 스크립트를 이용한 과거 포털 사이트(네이버,zum,구글) 실시간 검색어 수집 

  • 수집된 자료를 이용한 워드 클라우드 시각화

 

 완료

[사용 OS]

  • Windows 10

 

[사용 언어]

  • R v4.0.3

 

[소스 코드 - 인기 검색어 수집 코드]

library(RSelenium)
library(rvest)
library(stringr)
library(tidyverse)
library(data.table)
library(foreach)
library(httr)
library(webdriver)
library(seleniumPipes)
library(readxl)
library(foreach)
library(ggwordcloud)
library(wordcloud2)
library(htmlwidgets)
library(webshot)
# library(xlsx)
library(log4r)
library(readxl)
library(tcltk)
library(beepr)
#install.packages("noncompliance")
library(noncompliance)

Sys.setlocale("LC_ALL")
options(encoding = "UTF-8")
Sys.setenv(LANG = "en_US.UTF-8")


setWindowTab = function (remDr, windowId) {
  qpath = sprintf("%s/session/%s/window", remDr$serverURL, remDr$sessionInfo[["id"]])
  remDr$queryRD(qpath, "POST", qdata = list(handle = windowId))
}

getXpathText = function(xpath) {
  remDr$getPageSource()[[1]] %>%
    read_html() %>%
    rvest::html_nodes(xpath = xpath) %>%
    rvest::html_text() %>%
    str_replace_all(pattern = "\n", replacement = " ") %>%
    str_replace_all(pattern = "[\\^]", replacement = " ") %>%
    str_replace_all(pattern = "\"", replacement = " ") %>%
    str_replace_all(pattern = "\\s+", replacement = " ") %>%
    str_trim(side = "both")
}

getCssText = function(css) {
  remDr$getPageSource()[[1]] %>%
    read_html() %>%
    rvest::html_nodes(css = css) %>%
    rvest::html_text() %>%
    str_replace_all(pattern = "\n", replacement = " ") %>%
    str_replace_all(pattern = "[\\^]", replacement = " ") %>%
    str_replace_all(pattern = "\"", replacement = " ") %>%
    str_replace_all(pattern = "\\s+", replacement = " ") %>%
    str_trim(side = "both")
}

remDr = remoteDriver(
  remoteServerAddr = "localhost"
  , port = 5000L
  , browserName = "chrome"
)

remDr$open()
getRootHandle = remDr$getWindowHandles()

getUrl = "http://rank.ezme.net/zum"
remDr$navigate(getUrl)


# 오늘날짜 가져오기
webElem <- remDr$findElement(value = "*")
webElem$getElementAttribute("webElement fields")
getHtmlText = remDr$getPageSource()[[1]]

getDate = getHtmlText %>%
  read_html() %>%
  rvest::html_nodes("input") %>%
  html_attr("value") 
  

## 날짜 획득 ##
getDate_L1 <- getDate[2]


## 시작날짜 - 끝날짜 설정 ##
st_date = as.Date("2018-01-01")
ed_date = as.Date(getDate_L1)- 1
target_date = as.character(seq(st_date,ed_date,by = "days"))

data_full = data.frame()

aa = 0
## 수집 시작 ##
for (d in target_date) {
  
  aa = aa + 1
  
  ## 날짜 입력 및 클릭 ##
  remDr$findElement(using = "name",value = "day")$setElementAttribute("value", d)
  remDr$findElement(using= "class", value="mdl-button__ripple-container")$clickElement()
  ## 날짜 입력 및 클릭 ##
  
  Sys.sleep(1)
  
  webElem <- remDr$findElements(using = "class", value = "mdl-chip__text")
  resHeaders <- unlist(lapply(webElem, function(x){x$getElementText()}))
  
  if(length(webElem) == 240) {
    
    data_part = matrix(nrow = 10,ncol = 24)
    count = 0
    
    for (i in 0:23) {
      
      for (j in 1:10) {
        count = count + 1
        data_part[j,i+1] = resHeaders[count]
      }
      
    }
    
    colnameis = c("H00","H01","H02","H03","H04","H05","H06",
                  "H07","H08","H09","H10","H11","H12","H13",
                  "H14","H15","H16","H17","H18","H19","H20",
                  "H21","H22","H23")
    
    data_part <- as.data.frame(data_part)
    colnames(data_part) <- colnameis
    data_part$date = d
    data_part$site = "zum"
    data_part$rank = seq(1,10)
    
    write.table(data_part,"./zum.csv",sep=",",row.names = FALSE,append = TRUE,quote = FALSE)
    
  }
  
  if(aa %% 30 == 0){
    remDr$quit()
    Sys.sleep(3)
    remDr$open()
    getUrl = "http://rank.ezme.net/zum"
    remDr$navigate(getUrl)
    Sys.sleep(3)
    next
  }
  
}

 

[소스 코드 - 코로나 관련 키워드 추출 및 워드클라우드 시각화 코드]

## 명사 추출 함수 로드 (시작) ##
sub_extractNoun <- function(santence = santence, stopword = stopword){
  
  # 보통 명사 추출
  text <- extractNoun(santence)
  
  # 남아있는 특수문자, 숫자, 기호, 무의미한 자/모음 등을 제거 
  text <- gsub('[0-9a-zA-Z~!@#$%^&*()_+=?ㅜㅠㄱ-ㅎㅏ-ㅣ.<>‘’]','',text)
  
  # 불용어로 끝나는 단어 제거 (불필요한 조사 등을 제거하기 위함 -> 지속적인 재귀 호출을 이용하여 반복 처리)
  for (i in stopword) {
    text <- str_replace_all(string = text,pattern = paste0(i,"$"),"")
  }
  
  # 결과 리턴
  return(text)
  
}

data_full <- data.frame()

t_fn <- Sys.glob("./NEWS_OUTPUT/*")

count = 0

for (i in t_fn) {

  date = str_split(str_split(i,simplify = T,pattern = "./NEWS_OUTPUT/Naver_News_Keyword_홈트레이닝_")[2],
                   pattern="day_res.csv",simplify = T)[1]
  
  data_part <- fread(i,encoding = "UTF-8")

  if(dim(data_part)[1] == 0 && dim(data_part)[2] == 0){
    next
  }
  # count = count + 1
  
  data_part$date = date
  # data_part$newsnumber = count
  data_full <- rbind(data_full,data_part)
  
}

tail(data_full)
colnames(data_full)

data_full <- data_full %>%
  mutate(newsnumber = 1:nrow(data_full))

nounFull <- data.frame()

for (i in 1:dim(data_full)[1]) {
  
  print(i)
  nounPart <- as.data.frame(extractNoun(data_full$contentInfo[i]))
  colnames(nounPart)[1] = "word"
  nounPart$date = data_full$date[i]
  nounPart$newsnumber = i
  
  nounFull <- rbind(nounFull,nounPart)
  
}

tail(nounFull)
tail(data_full)


nounFullInfo <- dplyr::inner_join(nounFull,data_full,by=c("newsnumber","date"))

head(nounFullInfo)
colnames(nounFullInfo_L1)

nounFullInfo_L1 <- nounFullInfo %>%
  dplyr::filter(nchar(word) >= 2) %>%
  dplyr::select(-contentInfo,-urlInfo) %>%
  dplyr::filter(word == "홈트레이닝")

head(nounFullInfo_L1)
tail(nounFullInfo_L1)

as.Date(nounFullInfo_L1$date,format = "%Y%m%d")

###############
nounFullInfo_L1_before <- nounFullInfo_L1 %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date < as.Date("2020-01-20"))

nounFullInfo_L1_after <- nounFullInfo_L1 %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date >= as.Date("2020-01-20"))


##########################
nounFullInfo_before <- nounFullInfo %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date < as.Date("2020-01-20"))

nounFullInfo_after <- nounFullInfo %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date >= as.Date("2020-01-20"))
##########################

## 
dim(nounFullInfo_L1_before)[1] / length(unique(nounFullInfo_before$newsnumber))
dim(nounFullInfo_L1_after)[1] / length(unique(nounFullInfo_after$newsnumber))

length(unique(nounFullInfo_L1_before$newsnumber)) / length(unique(nounFullInfo_before$newsnumber))
length(unique(nounFullInfo_L1_after$newsnumber)) / length(unique(nounFullInfo_after$newsnumber))
## 


####################################################
nounFullInfo_L2_before2 <- nounFullInfo %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date < as.Date("2020-01-20")) 

before_newscount = length(unique(nounFullInfo_L2_before2$newsnumber))

nounFullInfo_L2_after2 <- nounFullInfo %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date >= as.Date("2020-01-20")) 

after_newscount = length(unique(nounFullInfo_L2_after2$newsnumber))

####################################################

nounFullInfo_L2_before <- nounFullInfo %>%
  dplyr::filter(nchar(word) >= 2) %>%
  dplyr::select(-contentInfo,-urlInfo) %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date < as.Date("2020-01-20")) %>%
  dplyr::group_by(word) %>%
  dplyr::summarise(freq = n(),
                   freqOfTotalNews = n() / before_newscount) %>%
  dplyr::arrange(desc(freq)) 

head(nounFullInfo_L2_before)

nounFullInfo_L2_after <- nounFullInfo %>%
  dplyr::filter(nchar(word) >= 2) %>%
  dplyr::select(-contentInfo,-urlInfo) %>%
  dplyr::mutate(date = as.Date(date,format = "%Y%m%d")) %>%
  dplyr::filter(date >= as.Date("2020-01-20")) %>%
  dplyr::group_by(word) %>%
  dplyr::summarise(freq = n(),
                   freqOfTotalNews = n() / after_newscount) %>%
  dplyr::arrange(desc(freq)) 

head(nounFullInfo_L2_after)

####################################################

asdf <- dplyr::inner_join(nounFullInfo_L2_before,nounFullInfo_L2_after,by=c("word")) %>%
        dplyr::arrange(desc(freqOfTotalNews.y-freqOfTotalNews.x))

####################################################

# 1. 워드클라우드 (전,후)
nounFullInfo_L3_before <- nounFullInfo_L2_before %>%
  dplyr::filter(!word %like% "[a-zA-Z1-9]") %>%
  dplyr::slice(1:100)  %>%
  dplyr::mutate(freq = (freq/max(freq)) * 10 ) %>%
  dplyr::select(-freq) %>%
  dplyr::mutate(freqOfTotalNews = round(freqOfTotalNews * 10,0))


# 플롯 #
hw = wordcloud2(data = nounFullInfo_L3_before,
                fontFamily='나눔바른고딕',
                minSize = 0,
                gridSize = 0)


# 이미지화 이후 저장 #
saveWidget(hw,"covid_before.html",selfcontained = F)
webshot::webshot("covid_before.html","covid_before.png",vwidth = 1200, vheight = 1000, delay = 30)
# 이미지화 이후 저장 #


nounFullInfo_L3_after <- nounFullInfo_L2_after %>%
  dplyr::filter(!word %like% "[a-zA-Z1-9]") %>%
  dplyr::slice(1:100) %>%
  dplyr::mutate(freq = (freq/max(freq)) * 10 ) %>%
  dplyr::select(-freq) %>%
  dplyr::mutate(freqOfTotalNews = round(freqOfTotalNews * 10,0))

# 플롯 #
hw = wordcloud2(data = nounFullInfo_L3_after,
                fontFamily='나눔바른고딕',
                minSize = 0,
                gridSize = 0)


# 이미지화 이후 저장 #
saveWidget(hw,"covid_after.html",selfcontained = F)
webshot::webshot("covid_after.html","covid_after.png",vwidth = 1000, vheight = 800, delay = 30)
# 이미지화 이후 저장 #


nounFullInfo_L3_join <- asdf %>%
  dplyr::filter(!word %like% "[a-zA-Z1-9]") %>%
  dplyr::mutate(freq = round((freqOfTotalNews.y - freqOfTotalNews.x)*100),0) %>%
  dplyr::slice(1:100) %>%
  dplyr::select(-freq.x,-freq.y,-freqOfTotalNews.x,-freqOfTotalNews.y,-`0`)

nounFullInfo_L3_join_L1 <- asdf 
colnames(nounFullInfo_L3_join_L1)[1:5] <- c("단어","코로나 이전 총 출현수","코로나 이전 뉴스 1건당 출현빈도","코로나 이후 총 출현수","코로나 이후 뉴스 1건당 출현빈도")
# 플롯 #
hw = wordcloud2(data = nounFullInfo_L3_join,
                fontFamily='나눔바른고딕',
                minSize = 0,
                gridSize = 0)

# 이미지화 이후 저장 #
saveWidget(hw,"covid_after_jointop.html",selfcontained = F)
webshot::webshot("covid_after_jointop.html","covid_after_jointop.png",vwidth = 1000, vheight = 800, delay = 30,selector = '#canvas')
# 이미지화 이후 저장 #


# 2. 막대그래프 (top10)
res_L1 <- asdf %>%
  dplyr::slice(1:10)

colnames(res_L1)[1:5] <- c("word","freqBefore","mFreqBefore","freqAfter","mFreqAfter")

#######################
asdf_1 <- res_L1 %>%
  dplyr::select(word,freqBefore,mFreqBefore) %>%
  dplyr::mutate(group = "코로나 이전")

asdf_2 <- res_L1 %>%
  dplyr::select(word,freqAfter,mFreqAfter) %>%
  dplyr::mutate(group = "코로나 이후")


colnames(asdf_1)[1:3] <- c("word","freq","mFreq")
colnames(asdf_2)[1:3] <- c("word","freq","mFreq")

asdf_res <- rbind(asdf_1,asdf_2)

asdf_res_L1 <- asdf_res %>%
  dplyr::mutate(rows = 1:nrow(asdf_res)) %>%
  dplyr::mutate(rows = as.character(rows)) %>%
  dplyr::mutate(word = as.factor(word))

glimpse(asdf_res_L1)
#######################
ggplot(asdf_res_L1, aes(x = reorder(as.factor(word), -mFreq, sum), y = mFreq, fill = as.factor(group))) + 
  geom_bar(stat = "identity", aes(fill = group) , position = "dodge") +
  xlab("키워드") + ylab("뉴스 1건당 평균 언급횟수") +
  ggtitle("코로나사태 이전 대비 빈도수 증가 명사 TOP 10") +
  theme_bw() +
  theme(
  plot.title = element_text(face = "bold", size = 18, color = "black")
  , axis.title.x = element_text(face = "bold", size = 16, colour = "black")
  , axis.title.y = element_text(face = "bold", size=16, colour = "black", angle = 90)
  , axis.text.x = element_text(face = "bold", size=14, colour = "black")
  , axis.text.y = element_text(face = "bold", size=14, colour = "black")
  , legend.justification = c(1, 1)
  , legend.key = element_blank()
  , legend.text = element_text(size = 14, face = "bold")
  , legend.title = element_text(face = "bold", size = 14, colour = "black")
  , legend.background=element_blank()
  , plot.margin = unit(c(0, 8, 0, 0), "mm")
  ) +
  ggsave("./OUT_FIG_20201009/res.png",dpi = 600,width = 12,height = 8) 


# 3. 엑셀시트
write.table(nounFullInfo_L2_before,"./OUT_FIG_20201009/코로나 이전 홈트레이닝 기사 키워드 단어 빈도.csv",row.names = F)
write.table(nounFullInfo_L2_after,"./OUT_FIG_20201009/코로나 이후 홈트레이닝 기사 키워드 단어 빈도.csv",row.names = F)
write.table(nounFullInfo_L3_join_L1,"./OUT_FIG_20201009/코로나 이전 대비 이후 홈트레이닝 기사 키워드 단어 빈도 변화 순위.csv",row.names = F)

 

[결과물]

  • 키워드 추출 결과 원본 예시

google.csv
3.20MB

 

  • 워드 클라우드 결과

 

 참고 문헌

[논문]

  • 없음

[보고서]

  • 없음

[URL]

  • 없음

 

 문의사항

[기상학/프로그래밍 언어]

  • sangho.lee.1990@gmail.com

[해양학/천문학/빅데이터]

  • saimang0804@gmail.com