반응형

     정보

    • 업무명     : 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
    반응형
    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기