본문으로 바로가기

2017.1.9 춥고 흐림. 


수정사항 : 2017-08-18, 인코딩 관련 소스라인 추가

               2017-09-11, 텍스트 마이닝 python korea 2017 에서 발표된 명사 추출 관련 자료 link , 데이터 기반의 명사 추출 기법

                               https://www.slideshare.net/kimhyunjoonglovit/pycon2017-koreannlp, 

| 들어가며 

오늘은 R을 이용해서 웹 데이터를 크롤링하고, 수집된 텍스트를 기반으로 연관 분석을 하는 과정을 공유할까 한다.

참고로 웹 크롤링은 웹 사이트가 빈번하기 바뀌기 때문에 작성하는 현 시점기준의 스크립트임을 밝힌다. 

혹 크롤링 대상 사이트에 변경이 있을 경우 해당 부분의 수정은 필요하다

| R을 활용한 웹 크롤링

오늘 해 볼 것은 

1. DAUM 의 영화 평 사이트에 기록된 감상평을 크롤링하고

2. 한 문장에 나오는 각 단어별 빈도수를 체크해 단어간의 연관성을 표현하려 한다.


우선 DAUM 영화 사이트를 들어가보자

DAUM 영화 (http://movie.daum.net/main/new)


다양한 영화들이 노출되어 있고 각 영화별 감상평을 확인할 수 있게 구성되어 있다.



많은 영화들 중에 "씽" 이라는 영화에 대해서 감상평을 크롤링 해보자


우선 영화 씽의 평점 탭을 눌러 리뷰페이지를 확인한다.   (http://movie.daum.net/moviedb/grade?movieId=99056)

다양한 리뷰가 짧게는 몇 자에서 부터 길게는 수백자 까지 적힌 글 들이 보인다.


미리 말하지만 리뷰중에 간혹 띄어쓰기를 하지 않고 올리는 사람들이 있다. 이런 경우 형태소 분석을 할 수 없어 대부분 버려진다.

혹 감상평 남기실 분들은 다른것은 몰라도 띄어쓰기 만큼은 지켜 줬으면 한다.;; 


이제 적혀있는 리뷰를 긁어올테데 여기부터 약간 html 에 대한 이해가 필요하다. 

사이트에서 마우스 우 클릭을 하고 소스보기를 누르면 페이지에 대한 html 이 보인다.

찾기로 리뷰가 시작되는 테그를 찾는다. 

필자가 이 글을 작성하는 시점에는 "<p class="desc_review">라는 테그로 리뷰가 시작하게 된다.   일단 여기까지 확인을 했으면 이제 R 로 돌아가 코딩을 시작해보자


먼저 필요한 패키지 부터 설치한다.

install.packages(c('rvest','httr','KoNLP','stringr','tm','qgraph','xml2'))

library(rvest)

library(httr)

library(KoNLP)

library(stringr)

library(tm)

library(qgraph)

library('xml2')



패키지 설치가 끝났으면 본격적인 크롤링을 위한 코드를 작성한다. 

url_base <- 'http://movie.daum.net/moviedb/grade?movieId=99056&type=netizen&page='   # 크롤링 대상 URL


all.reviews <- c() 

for(page in 1:500){    ## 500페이지 까지만 수집 (본인이 나름대로 설정하면 됨) 

  url <- paste(url_base, page, sep='')   #url_base의 뒤에 페이지를 1~500 까지 늘리면서 접근

  htxt <- read_html(url)                       # html 코드 불러오기

  comments <- html_nodes(htxt, 'div') %>% html_nodes('p')  ## comment 가 있는 위치 찾아 들어가기 

  reviews <- html_text(comments)               # 실제 리뷰의 text 파일만 추출

  reviews <- repair_encoding(reviews, from = 'utf-8')  ## 인코딩 변경

  if( length(reviews) == 0 ){ break }                              #리뷰가 없는 내용은 제거

  reviews <- str_trim(reviews)                                      # 앞뒤 공백문자 제거

  all.reviews <- c(all.reviews, reviews)                          #결과값 저장

}


##불필요 내용 필터링

all.reviews <- all.reviews[!str_detect(all.reviews,"평점")]   # 수집에 불필요한 단어가 포함된 내용 제거


주요 코드를 설명하자면


  • url_base 는 크롤링 대상 최 상위 url 이다.  url_base 뒤에 page= 이 보일 텐데 그 뒤로 페이지 번호가 붙으면서 리뷰 페이지가 넘어가게 된다.
    따라서 for 문으로 반복하여 각 페이지를 방문하면서 클롤링을 해야 한다.    
    url_base 에 페이지 번호를 붙이는 것은 "paste" 명령어가 처리한다.

  • read_html 을 해당 URL의 html 을 header 와 body 로 가져온다.

  • html_nodes 는 테그를 찾아주는 함수다.
    리뷰가 적혀 있는 "<p class="desc_review">" 테그 위치까지 찾아 내려가야 한다.
    필자가 확인하기로 <div> 테그가 먼저 desc_review 를 감싸고 있었기 때문에 <div> 를 먼저 찾는다.  
    그리고 하위의 <p> 테그를 접근해야 하기 때문에  %>%  를 붙여서 html_nodes('p')를 찾는다.
    %>% 는 선행의 결과를 후행으로 이어서 처리할 수 있도록 도와주는 역할이라고 보면 된다.
    ※ 만약 사이트가 다르거나 경로가 바뀌었다면 리뷰를 계층적으로 찾아들어갈 수 있도록 html_nodes만 여러차례 변경하면 된다.

  • html_nodes 까지 찾으면 이제 실제 리뷰만 긁어가면 되는데 html_text 명령어가 이를 처리한다.

  • 리뷰가 없는 경우와 앞뒤 공백등의 불필요한 내용은 제거한다.

  • 특이하게도 리뷰를 보게되면 "내 평점이 없습니다. 평점을 등록해주세요." 라는 문구가 하단에 지속 반복 된다.
    이는 시스템에서 default 로 넣은 문구이기 때문에 분석 대상이 아니다.
    크롤링 하다보니 같이 들어온 문구이지만 분석에는 관련이 없어서 제거가 필요하다.
    str_detect명령어로 "평점" 이 들어가는 내용은 전부 제거 처리를 했다. 

| 단어 연관 분석

위 내용이 모두 끝나면 명사와 형용사를 추출하는 function 을 만든다.

SimplePos09가 각 문장의 형태를 분리하게 되고 str_match가  가-힣 까지의 단어와 함께  N(명사) , P(형용사)  만들 가져오도록 추출한다. 

## 명사/형용사 추출 함수 생성

ko.words <- function(doc){

  d <- as.character(doc)

  pos <- paste(SimplePos09(d))

  extracted <- str_match(pos, '([가-힣]+)/[NP]')

  keyword <- extracted[,2]

  keyword[!is.na(keyword)]

}

  • str_match 는 해당 문자가 매치되는 경우만 가져오도록 해준다.



이제 분리된 단어를 기준으로 Corpus 라는 일종의 말뭉치로 끊어낸 뒤 각 단어별 노출 빈도수를  TermDocumentMatrix 를 계산한다.

options(mc.cores=1)    # 단일 Core 만 활용하도록 변경 (옵션)

cps <- Corpus(VectorSource(all.reviews))  

tdm <- TermDocumentMatrix(cps,   

                          control=list(tokenize=ko.words,   ## token 분류시 활용할 함수명 지정

                                       removePunctuation=T,

                                       removeNumbers=T,

                                       wordLengths=c(2, 6),  

                                       weighting=weightBin))  

#최종결과 확인
dim(tdm)
tdm.matrix <- as.matrix(tdm)
Encoding(rownames(tdm.matrix)) <- "UTF-8"
rownames(tdm.matrix)[1:100]
  • Corpus는 말뭉치로 텍스트 마이닝을 처리 하기위한 단위라고 생각하면 된다.
  • TermDocumentMatrix에 Corpus를 넣어야 한다.
  • wordLengths 는 최대 최소 단어의 길이를 지정하는 것으로 보통 한글이 그리 길지 않기 때문에 그것을 고려해서 설정한다. 너무 길 경우 단어가 아닌 문장들이 섞여 들어올 수 있다. 참고로 한글은 2자리 씩 차지하기 때문에 숫자로 표현시 곱하기 2를 해야 한다.
  • weightBin은 한 문장에서 동일하게 반복되는 말의 경우를 여러차례 카운트 하는 것이 하니라 한번으로 만 카운트 하게 된다. 
    예를 들어 너무 너무 좋았다 라는 표현도 너무라는 의미가 한번쓰인 것이 중요하지 강조한다고 여기서는 큰 의미가 없기 때문에 제외한다.


위 결과를 보게 되면 다음과 같이 추출된 단어가 보인다.

TermDocumentMatrix는 단어가 Document 별로 얼마나 나왔는지를 Matrix 형태로 보여주는 것이라 할 수 있다.

> tdm.matrix

              Docs

Terms          1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

  가능한       0 0 0 0 0 0 0 0 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

              Docs

Terms          36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

  가능한        0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

              Docs ... ...


> rownames(tdm.matrix)[1:100]

  [1] "가능한"       "가득"         "가득한"       "가르치"       "가사도"       "가사보고"     "가장강력한"  

  [8] "가장큰"       "가족과함"     "가족들"       "가족애"       "가족영화"     "가지"         "간만"        

 [15] "간아깝"       "간절한"       "감동"         "감동받긴"     "감동애"       "감동적"       "감상하"      

 [22] "감점"         "감정이입"     "강력한"       "강렬한"       "강추"         "강추합니다"   "개봉하"      ... ...




이제 이 결과를 시각화 하기 위해 각 단어별로 노출 빈도수가 많은 20개의 단어만을 추려낸다. 

freq.words 간 행렬 곱을 하여 서로 많이 나온 단어끼리는 수치가 높게 나오도록 계산한다.

#자주 쓰이는 단어 순으로 order 처리

word.count <- rowSums(tdm.matrix)  ##각 단어별 합계를 구함

word.order <- order(word.count, decreasing=T)  #다음으로 단어들을 쓰인 횟수에 따라 내림차순으로 정렬

freq.words <- tdm.matrix[word.order[1:20], ] #Term Document Matrix에서 자주 쓰인 단어 상위 20개에 해당하는 것만 추출

co.matrix <- freq.words %*% t(freq.words)  #행렬의 곱셈을 이용해 Term Document Matrix를 Co-occurence Matrix로 변경

> co.matrix

            Terms

Terms        영화 노래 감동 재미 기대 음악 아이들 재미있 나오 스토리 느끼 지루 생각 귀엽 애니메이션 어른 장면

  영화         30    8    4    9    5    6      2      3    4      3    4    1    3    2          1    1    1

  노래          8   29    3    3    1    3      2      2    3      2    3    2    4    3          1    1    2

  감동          4    3   15    3    2    3      0      1    0      1    1    0    1    0          1    1    0

  재미          9    3    3   14    3    3      1      2    2      2    2    2    0    0          1    0    1

  기대          5    1    2    3   12    1      0      0    0      1    1    2    2    0          0    1    1

  음악          6    3    3    3    1   12      0      1    2      1    2    0    2    1          2    0    0

  아이들        2    2    0    1    0    0     11      2    1      1    2    1    1    0  


결과적으로 각 단어끼리 노출 빈도수에 따라 많이 노출되는 경우는 연관성이 높다 판단하고 그렇지 않은것은 상대적으로 적다고 판단할 수 있다.

| 최종 출력

최종적으로는 qgraph 명령어로 시각화 한다.


qgraph(co.matrix,

       labels=rownames(co.matrix),   ##label 추가

       diag=F,                       ## 자신의 관계는 제거함

       layout='spring',              ##노드들의 위치를 spring으로 연결된 것 처럼 관련이 강하면 같이 붙어 있고 없으면 멀리 떨어지도록 표시됨

       edge.color='blue',

       vsize=log(diag(co.matrix))*2) ##diag는 matrix에서 대각선만 뽑는 것임. 즉 그 단어가 얼마나 나왔는지를 알 수 있음. vsize는 그 크기를 결정하는데 여기 인자값으로 단어가 나온 숫자를 넘겨주는 것임. log를 취한것은 단어 크기의 차이가 너무 커서 log를 통해서 그 차이를 좀 줄여준것임. 


아래 결과를 보면 영화, 노래 기대, 재미, 감동 등의 단어가 자주 그리고 서로 많이 나오는 것을 시각적으로 확인할 수 있다.
아래 그래프에 단어별로 긍/부정을 섞을 경우 영화에 대한 사람들이 느끼는 긍/부정률이 어느정도 인지도 알 수 있다. 
감성 분석에 대한 것은 다른 포스팅에서 언급하고자 한다.





[인코딩 관련 수정사항]  2017-08-18


아래 코드를 수행하다보면 all.reviews 까지는 정상적으로 글씨가 보이는데

TermDocumentMatrix 를 통과하고 부터는 한글이 전부 깨져서 보인다. 


원인은 인코딩 문제다.

Encoding(all.reviews)  을 실행 시키면 글씨가 노출된 부분에 UTF-8로 된 것을 알 수 있다. 

수집된 글의 인코딩은 UTF-8이란 얘기다. 

그러나 localeToCharset()  을 수행하면 아마도 본인 Local의 인코딩은 CP949로 보일 것이다.


어떤 이유에서인지 텍스트 마이닝 수행하다 중간부터 인코딩이 UTF-8에서 다른 것으로 변경되는 것으로 보이는데

이를 해결하려면 

코드 중간에 Encoding(rownames(tdm.matrix)) <- "UTF-8" 이 추가되어야 한다.

이 부분은 위 코드에도 수정해 놓았으니 참고 하면 될 것 같다.


[인코딩 관련 수정사항2 ]  2018-08-19

만약 맥북 이면서 위 조치사항을 했음에도 한글 오류가 나올 경우

par(family="Apple SD Gothic Neo")   ## mac option

을 추가한다.

| 전체 코드

install.packages(c('rvest','httr','KoNLP','stringr','tm','qgraph','xml2'))

library(rvest)

library(httr)

library(KoNLP)

library(stringr)

library(tm)

library(qgraph)

library('xml2')


url_base <- 'http://movie.daum.net/moviedb/grade?movieId=99056&type=netizen&page='   # 크롤링 대상 URL


all.reviews <- c() 

for(page in 1:500){    ## 500페이지 까지만 수집 (본인이 나름대로 설정하면 됨) 

  url <- paste(url_base, page, sep='')   #url_base의 뒤에 페이지를 1~500 까지 늘리면서 접근

  htxt <- read_html(url)                       # html 코드 불러오기

  comments <- html_nodes(htxt, 'div') %>% html_nodes('p')  ## comment 가 있는 위치 찾아 들어가기 

  reviews <- html_text(comments)               # 실제 리뷰의 text 파일만 추출

  reviews <- repair_encoding(reviews, from = 'utf-8')  ## 인코딩 변경

  if( length(reviews) == 0 ){ break }                              #리뷰가 없는 내용은 제거

  reviews <- str_trim(reviews)                                      # 앞뒤 공백문자 제거

  all.reviews <- c(all.reviews, reviews)                          #결과값 저장

}


##불필요 내용 필터링

all.reviews <- all.reviews[!str_detect(all.reviews,"평점")]   # 수집에 불필요한 단어가 포함된 내용 제거


Encoding(all.reviews)


options(encoding="utf-8")

## 명사/형용사 추출 함수 생성

ko.words <- function(doc){

  d <- as.character(doc)

  pos <- paste(SimplePos09(d))

  extracted <- str_match(pos, '([가-힣]+)/[NP]')

  keyword <- extracted[,2]

  keyword[!is.na(keyword)]

}



options(mc.cores=1)    # 단일 Core 만 활용하도록 변경 (옵션)

cps <- Corpus(VectorSource(all.reviews))  

tdm <- TermDocumentMatrix(cps,   

                          control=list(tokenize=ko.words,   ## token 분류시 활용할 함수명 지정

                                       removePunctuation=T,

                                       removeNumbers=T,

                                       wordLengths=c(2, 6),  

                                       weighting=weightBin

                                       ))  

#최종결과 확인

dim(tdm)

tdm.matrix <- as.matrix(tdm)

Encoding(rownames(tdm.matrix)) <- "UTF-8"


word.count <- rowSums(tdm.matrix)  ##각 단어별 합계를 구함

word.order <- order(word.count, decreasing=T)  #다음으로 단어들을 쓰인 횟수에 따라 내림차순으로 정렬

freq.words <- tdm.matrix[word.order[1:20], ] #Term Document Matrix에서 자주 쓰인 단어 상위 20개에 해당하는 것만 추출

co.matrix <- freq.words %*% t(freq.words)  #행렬의 곱셈을 이용해 Term Document Matrix를 Co-occurence Matrix로 변경


par(family="Apple SD Gothic Neo")   ## mac option

qgraph(co.matrix,

       labels=rownames(co.matrix),   ##label 추가

       diag=F,                       ## 자신의 관계는 제거함

       layout='spring',              ##노드들의 위치를 spring으로 연결된 것 처럼 관련이 강하면 같이 붙어 있고 없으면 멀리 떨어지도록 표시됨

       edge.color='blue',

       vsize=log(diag(co.matrix))*2) ##diag는 matrix에서 대각선만 뽑는 것임. 즉 그 단어가 얼마나 나왔는지를 알 수 있음. vsize는 그 크기를 결정하는데 여기 인자값으로 단어가 나온 숫자를 넘겨주는 것임. log를 취한것은 단어 크기의 차이가 너무 커서 log를 통해서 그 차이를 좀 줄여준것임. 


| 추가사항

추가로 python korea 2017에서 발표된 자연어 처리 관련 명사 추출 및 토크나이징 관련 방표 자료 링크를 걸어둔다.

Pycon2017 koreannlp : https://www.slideshare.net/kimhyunjoonglovit/pycon2017-koreannlp

pycon2017koreannlp-170809135945.pdf


| 참고 자료

마인드 스케일 텍스트 마이닝 교육



도움이 되셨다면 공감버튼을 살포시~


댓글을 달아 주세요

  1. 이전 댓글 더보기
  2. 오승현 2017.06.01 11:39

    안녕하세요! R 이용하여 데이터 분석 시작해본 입문자입니다. 등록해주신 소스코드 잘봤습니다!
    소스코드 컴파일 결과 값이 아무것도 인식이 안되서 혹시 어디서 문제인지 디버깅 하는것이 너무 힘들어 질문 드립니다.

    > library(rvest)
    > library(httr)
    > library(KoNLP)
    > library(stringr)
    > library(tm)
    > library(qgraph)
    > library('xml2')
    > ##크롤링 대상 지정
    > url_base <- 'http://movie.daum.net/moviedb/grade?movieId=92720&type=netizen&page='
    > all.reviews <- c()
    > for(page in 1:10)
    + {
    + url <- paste(url,page,sep='')
    + htxt <- read_html(url)
    + comments <- html_nodes(htxt,'div') %>% html_nodes('p')
    + reviews <- html_text(comments)
    + reviews <- repair_encoding(reviews,from='utf-8')
    + if(length(reviews)==0)
    + {
    + break
    + }
    + reviews <- str_trim(reviews)
    + all.reviews <- c(all.reviews, reviews)
    + }
    > reviews
    character(0)
    > ##불필요한 내용 필터링
    > ##all.reviews <- all.reviews[!str_detect(all.reviews,"평점")]
    >
    > ##명사 형용사만 추출하는 함수 생성
    > ko.words <- function(doc)
    + {
    + d <- as.character(doc)
    + pos <- paste(SimplePos09(d))
    + extracted<- str_match(pos,'([가-힣]+)/[NP]')
    + keyword <- extracted[,2]
    + keyword[!is.na(keyword)]
    + }
    >
    > options(mc.cores=1)
    > cps <- Corpus(VectorSource(all.reviews))
    > tdm <- TermDocumentMatrix(cps, control = list(tokenize = ko.words,
    + removePunctuation = T,
    + removeNumbers=T,
    + wordLengths=c(2,6),
    + weighting=weightBin))
    > dim(tdm)
    [1] 0 0
    > tdm.matrix <- as.matrix(tdm)
    > tdm.matrix
    <0 x 0 matrix>
    > rownames(tdm.matrix)[1:100]
    NULL
    >
    > ##Frequency
    > word.count <- rowSums(tdm.matrix)
    > word.order <- order(word.count,decreasing=T)
    > freq.words <- tdm.matrix[word.order[1:20],]
    > co.matrix <- freq.words %*% t(freq.words)
    >
    > co.matrix
    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
    [1,] 0 0 0 0 0 0 0 0 0 0
    [2,] 0 0 0 0 0 0 0 0 0 0
    [3,] 0 0 0 0 0 0 0 0 0 0
    [4,] 0 0 0 0 0 0 0 0 0 0
    [5,] 0 0 0 0 0 0 0 0 0 0
    [6,] 0 0 0 0 0 0 0 0 0 0
    [7,] 0 0 0 0 0 0 0 0 0 0
    [8,] 0 0 0 0 0 0 0 0 0 0
    [9,] 0 0 0 0 0 0 0 0 0 0
    [10,] 0 0 0 0 0 0 0 0 0 0
    [11,] 0 0 0 0 0 0 0 0 0 0
    [12,] 0 0 0 0 0 0 0 0 0 0
    [13,] 0 0 0 0 0 0 0 0 0 0
    [14,] 0 0 0 0 0 0 0 0 0 0
    [15,] 0 0 0 0 0 0 0 0 0 0
    [16,] 0 0 0 0 0 0 0 0 0 0
    [17,] 0 0 0 0 0 0 0 0 0 0
    [18,] 0 0 0 0 0 0 0 0 0 0
    [19,] 0 0 0 0 0 0 0 0 0 0
    [20,] 0 0 0 0 0 0 0 0 0 0
    [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19]
    [1,] 0 0 0 0 0 0 0 0 0
    [2,] 0 0 0 0 0 0 0 0 0
    [3,] 0 0 0 0 0 0 0 0 0
    [4,] 0 0 0 0 0 0 0 0 0
    [5,] 0 0 0 0 0 0 0 0 0
    [6,] 0 0 0 0 0 0 0 0 0
    [7,] 0 0 0 0 0 0 0 0 0
    [8,] 0 0 0 0 0 0 0 0 0
    [9,] 0 0 0 0 0 0 0 0 0
    [10,] 0 0 0 0 0 0 0 0 0
    [11,] 0 0 0 0 0 0 0 0 0
    [12,] 0 0 0 0 0 0 0 0 0
    [13,] 0 0 0 0 0 0 0 0 0
    [14,] 0 0 0 0 0 0 0 0 0
    [15,] 0 0 0 0 0 0 0 0 0
    [16,] 0 0 0 0 0 0 0 0 0
    [17,] 0 0 0 0 0 0 0 0 0
    [18,] 0 0 0 0 0 0 0 0 0
    [19,] 0 0 0 0 0 0 0 0 0
    [20,] 0 0 0 0 0 0 0 0 0
    [,20]
    [1,] 0
    [2,] 0
    [3,] 0
    [4,] 0
    [5,] 0
    [6,] 0
    [7,] 0
    [8,] 0
    [9,] 0
    [10,] 0
    [11,] 0
    [12,] 0
    [13,] 0
    [14,] 0
    [15,] 0
    [16,] 0
    [17,] 0
    [18,] 0
    [19,] 0
    [20,] 0

    어디서 문제가있는지 해결방안좀 부탁드립니다 ㅠㅠ

    • BlogIcon 쿡북 2017.06.03 22:46 신고

      죄송합니다. 코드를 주시면... 하나하나 확인해 드리기가 어렵네요... ㅜㅜ 0으로 나온것으로 봐서 크롤링이 잘못된것 같은데 라인별로 하나씩 출력해서 찾아가시기를 권장 드립니다.

  3. Cck 2017.06.03 01:48

    상세한 글 고맙습니다 ~^^

  4. BlogIcon iia_hannah 2017.06.03 22:19 신고

    안녕하세요!! 포스팅 글 보고 도움 많이 얻고 가요~
    소스코드 보고 데이터를 그래프로 출력하는 부분 까지는 했는데요~
    그래프의 한글이 'ㅁㅁ' 이렇게 깨져서 들어가더라구요... ㅠㅠ..
    UTF-8로 인코딩 설정도 해준 것 같은데... 왜그런지 알 수 있을까요?

    • BlogIcon 쿡북 2017.06.03 22:48 신고

      한글이 깨지는 것은 제 경험상 전부 인코딩 문제로 알고 있습니다. 실제 변경했다 하더라도 아마 내부적으로 제대로 설정이 안 먹은 경우가 더러 있던데 , 한글만 유독 문제가 된다면 인코딩 쪽으로 한번 찾아보셔서 더 접근해 보시면 될 것 같습니다. 감사합니다.

  5. BlogIcon iia_hannah 2017.06.04 00:18 신고

    답변 해주셔서 감사해요^^
    R 내에서는 한글 인코딩이 잘 먹히는데 꼭 팝업으로 그래프를 띄울 때 한글이 깨지더라구요!
    구글에서 찾아보니 .Rprofile 파일 환경설정을 해주니 한글이 잘 보여요! 맥에서 해서 한글 인코딩 문제가 좀 있네요!
    ^^주말 잘 보내세요

  6. 이지 2017.06.07 16:17

    혹시
    library(KoNLP)
    했을때
    Checking user difined dictionary!
    오류는 왜 뜨는 건지 알 수 있을 까요?

    • 은철 2017.08.07 14:08

      지나가다가 그냥 아는 것이 나와서 답변드려요. 사전 지정해주라는 것인데, useSejongDic()
      이거 실행해서 사용하심 될것같습니다.

  7. 2017.06.09 23:58

    co.matrix <- freq.words %*% t(freq.words) 이후에 co.matrix 결과값이 0으로만나오네요 뭐가문제일까요?
    그전까지는 잘된거같은데..

    • BlogIcon 쿡북 2017.06.30 14:01 신고

      안녕하세요? 답변이 너무 늦었네요. 저도 댕 님의 환경을 직접 보시 않고서는 0으로만 나온 이유를 그냥 봐서는 알기 어려울 것 같습니다... 도움 못드려 죄송합니다.

  8. mkp9942s 2017.06.28 14:38

    안녕하세요.
    위 예제를 사용해서 연관분석을 테스트를 해보았는데, 많은 도움이 되었습니다.
    그런데 몇가지 궁금한게 있는데, 구글에서도 정보가 많이 없어 질문드립니다.
    위 예제 결과대로 제대로 출력이 되는데 여기에 점들이 클수록 색이 찐해지는 옵션과 단어가 몇개이상일 시 선의 색깔이 다르게하는 옵션을 추가하고 싶습니다.
    간단한 예제라도 알려주시면 감사하겠습니다.(참고할 만한 URl이라도 감사합니다.ㅠㅠ)
    qgraph의 옵션 정보를 찾지를 못해서 이렇게 문의드립니다.

    • BlogIcon 쿡북 2017.06.30 14:04 신고

      안녕하세요? 답변이 늦어 죄송합니다.
      qgraph 예제를 찾아보니 아래 사이트가 가장 잘 나와 있네요
      http://sachaepskamp.com/qgraph/examples
      찾으시는게 있는것 같은데, 한번 참고해 보셔도 좋을 듯 합니다. 감사합니다.

  9. BlogIcon Bj 2017.06.30 12:34

    안녕하세요. 네트워크 그래프하고 연관분석에 관련해서 공부하고 있는 학생입니다.
    여러 여쭤보고 싶은게 있어서 이렇게 댓글을 달게 됬습니다.
    마지막에 보여주신 네트워크그래프에서 기대 감동 노래 재미 등
    선이 굵고 영화라는 단어와 가까이 있는 것을 볼수있습니다.!
    그렇다면 해석에 있어서
    '영화'는 가까이 있는 기대 감동 노래 등이 '영화'와 연관성이 높다고 할 수 있나요?

    실제로 현재 진행중인 것에서 네트워크 그래프는 잘나왔는데
    apriori 함수를 이용해서 연관분석도 진행하는데.
    두 결과가 전혀 관련이 없어서 여쭤봅니다ㅜㅜ

    • BlogIcon 쿡북 2017.06.30 14:07 신고

      안녕하세요? 연관성의 정의를 내려야 할 것 같은데 위 예제에서의 연관성은 "단어 노출이 얼마나 같이 되었느냐"로 해석됩니다.
      사실 노출이 서로 빈번하게 나왔더라도 각각의 단어간의 의미가 노출 수 만큼 완전 연관성이 있다고는 보기 어려울 수 있습니다. 또한 문맥상 단어의 의미가 서로 다르게 쓰일 수도 있기 때문에 더 그렇습니다. 따라서 뽑고자 하시는 단어에 대한 연관성은 다각도로 살펴보시는게 어떨까 싶네요.
      감사합니다.

  10. Bj 2017.06.30 18:17

    답변감사합니다!
    여기답글에는사진이안올라가서어떻게ㅣ보여드려야할지..

    실례지만 몇가지 여쭤봐도되련지요ㅜㅜ..

    제가 현재 네트워크그래프 그린매트릭스는
    댓글1 댓글2.....댓글17849
    단어1
    단어2
    ....
    단어30

    이렇게 구성되어있습니다 각행과열에는
    댓글1에
    단어1~30까지각각의 빈도수가들어가있구요!
    이거 위에서 알려주신것처럼
    m%*%t(m) 로
    30 by 30 메트릭스를만들어서 본 포스팅처럼 해서
    그려서 너무이쁘게잘나왔습니다!

    그럼이 네트워크그래프의해석으로는
    가운데위치할수록 중요하다? 빈도수가많다?
    원의크기가클수록 ?
    원과원사이가가까울수록 연관성이높다? 같이등장한다?
    선이굵을수록 연관성이높다? 같이등장한다?
    처럼 하나를해석하면다른쪽이같은의미인지요..?

    만약 두단어가동시에출현하는정도로봤을때해석을 두단어가동시에등장하므로 ..연관성..
    이런식으로해석할수록 더들어가는 것 같습니다ㅜㅜ

    그리고연관성법칙에사용한메트릭스는

    리스트1
    리스트2
    ....
    리스트17849
    해서 각리스트는댓글이고 각댓글들에대해서
    단어1~30개 의단어들의 나타나면 있고 안나타나면 없습니다

    리스트1 ~ 재미 기대
    리스트2 ~ 재미 사랑 배우
    이런식으로해서 transactions형태로변경해서 하였습니다ㅜㅜ

    이 두 분석이 다르다고한다면 두개를같이사용하지못하는것인가요?
    이쪽브분이막혀서다음으로못넘어가고있어서ㅜㅜ

    번거롭게 긴글읽어주셔서감사합니다ㅜ

    • BlogIcon 쿡북 2017.07.01 19:57 신고

      안녕하세요? 죄송하지만 어떤 부분에서 어려움을 겪으시는지 제가 이해하기는 어렵네요. 그냥 추정해서 말씀 드리면 아마도 단어간 연관성 분석을 하시는듯 한데, 많은 가정등을 깔고 있는 느낌이네요.
      단어가 동시에 노출되었다고 하더라도 무조건 두 단어의 연관성이 높다고 해석하시는 것은 무리가 있을듯 합니다. 위 예제는 영화에 대한 댓글의 상호 노출 빈도수를 네트워크로 표현했을 뿐 그것이 곧 "단어간 연관성" 으로 해석하시면 안될 것 같네요. 특히 한글의 경우 문맥상 내포하는 뜻도 달라서 저렇게 2차원 으로 표현된 단어간 연관성은 무리가 따릅니다.
      그리고 아래 리스트의 경우는 어떤 내용을 표현하신 것인지 모르겠는데, 저것 역시 그냥 리스트 내에서 주요 단어의 노출 빈도수로 밖에는 보이지 않습니다. 빈도수가 높을 수록 단어간 연관성이 높다는 것은 가정은 될 수 있어도 그것으로 결론을 지으면 해석이 이상해 질 것 같네요. 감사합니다.

    • BlogIcon 비밀글 2017.07.02 00:45

      비밀글입니다.

  11. 1234 2017.07.10 20:49

    #install.packages('rvest')
    library('rvest')
    library('httr')
    url_base <- 'http://swindow.naver.com/itemdetail/1001679053?NaPm=ct%3Dj4y1v0ow%7Cci%3Dfd4be4869fbf588396107a18e6acc66a15ade460%7Ctr%3Dslsl%7Csn%3D234661%7Cic%3D%7Chk%3D070b971c25abfa67d9b9aea719665af1eb704017&inflow=wsl' # 크롤링 대상 URL




    all.reviews <- c()

    for(page in 1:10){ ## 500페이지 까지만 수집 (본인이 나름대로 설정하면 됨)

    url <- paste(url_base, page, sep='') #url_base의 뒤에 페이지를 1~500 까지 늘리면서 접근

    htxt <- read_html(url) # html 코드 불러오기

    comments <- html_nodes(htxt, 'di') %>% html_nodes('desc') ## comment 가 있는 위치 찾아 들어가기

    reviews <- html_text(comments) # 실제 리뷰의 text 파일만 추출

    reviews <- repair_encoding(title, from = 'utf-8') ## 인코딩 변경

    if( length(title) == 0 ){ break } #리뷰가 없는 내용은 제거

    reviews <- str_trim(title) # 앞뒤 공백문자 제거

    all.reviews <- c(all.title, title) #결과값 저장

    }

    안녕하세요
    http://swindow.naver.com/itemdetail/1001679053?NaPm=ct%3Dj4y1v0ow%7Cci%3Dfd4be4869fbf588396107a18e6acc66a15ade460%7Ctr%3Dslsl%7Csn%3D234661%7Cic%3D%7Chk%3D070b971c25abfa67d9b9aea719665af1eb704017&inflow=wsl
    상품평 일반,프리미엄 크롤링 하려고 하는데 url만 살짝 바꿨는데 잘못바꿧는지 먹히지가 않아요...
    제가 무엇을 잘못했는지 알 수 있을 까요

  12. 서누 2017.07.20 15:37

    일단 좋은글 감사드립니다.

    저도
    위에 분과 동일한 현상인데요,
    기존 문장에서는 정상적으로 한글이 나오는데,

    ##########단어분리하여 corpus라는 말뭉치 단위로 단어별 빈도수 추출########
    ##########TermDocumentMatrix 계산########


    tdm<- TermDocumentMatrix(cps,
    control=list(tokenize = ko.words,
    removePunctuation=T,
    removeNumbers=T,
    wordLengths=c(2,6),
    weighting=weightBin))

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

    여기문장 돌고나면 글자가 깨지고 인코딩을 바꿔도 답이 없네요..

    좋은 소스로 실무에 적용중이었는데... 아쉽습니다.^^;;

    좋은글들 잘 보고 많은 도움 얻고갑니다.

    혹 관련에러에 대한글 보신것 있으시면 공유부탁드리겠습니다.

    • BlogIcon 쿡북 2017.08.18 00:37 신고

      답이 늦어서 죄송합니다. ;;
      인코딩 문제가 많네요? 저는 안깨지는데 아무래도 OS 상의 인코딩하고 뭔가 충돌이 나는듯 합니다. 인코딩 건은 환경상 차이때문에 정확하게 답변 드리기가 어려울 것 같습니다. 감사합니다.

    • BlogIcon 쿡북 2017.08.19 00:15 신고

      인코딩 문제 수정해놨습니다. 위에 코드 다시 확인해보세요 감사합니다.

  13. 초로로로록 2017.07.27 10:52

    안녕하세요 상세한 포스팅 감사합니다^^
    다름이 아니라 크롤링을 할 때는 한글이 깨지지 않는데
    tdm을 하는순간 글자가 다 깨져버리네요ㅜㅜ 구글로 찾아서 해봤지만 계속 깨지더라구요ㅜㅜ
    혹시 어떻게 해결해야 하는지 알 수 있을까요?

    • BlogIcon 쿡북 2017.08.18 00:38 신고

      답변이 늦어서 정말 죄송합니다.
      위에 답변 달은것 처럼 환경 문제가 아닐까 싶네요. 저는 안깨지는데...
      도움을 못드려서 죄송합니다.

    • BlogIcon 쿡북 2017.08.19 00:15 신고

      인코딩 문제 수정해놨습니다. 위에 코드 다시 확인해보세요 감사합니다.

  14. 산림정 2017.08.17 11:29

    안녕하세요! 올려주신 자료 덕분에 텍스트마이닝 공부를 잘 하고 있습니다. 따라하는 도중에 마지막 명령어에서 아래와 같은 에러 메세지가 떳습니다 [Error in qgraph(co.matrix, labels = rownames(co.matrix), diag = F, layout = "spring", :
    could not find function "qgraph"] 도대체 이유를 몰라서 여쭤보고 싶습니다 ㅠㅠ

    • BlogIcon 쿡북 2017.08.18 00:41 신고

      저는 못본 에러네요. qgraph 를 찾을 수 없다는 오류인데 추측에는 이미 깔아 놓으신 패키지랑 충돌 나는게 아닐까 싶습니다. library 올리신거 초기화 하신후에 다시 해보시면 어떨까요?

  15. blue1blue 2017.09.10 17:43

    안녕하세요. 코드 공유해주셔서 감사합니다. 다른 부분은 잘 되는데, 하다가 궁금한 점이 있어 문의 드려봅니다.
    한국어이다 보니깐 주어가 여러가지 형태로 나오는건 어떻게 하면 깔끔하게 잘 묶이는가요?
    예를 들어 맥도날드가 있다면 co.matrix(동시출현)에 맥도날드가 맥도날드는 등 여러개가 나오는데 이를 한꺼번에 묶는 방법을 알려주시면 감사합니다.

    • BlogIcon 쿡북 2017.09.11 00:24 신고

      좋은 질문이신데요. 사실 공유된 R 코드만으로는 좀 한계가 있습니다. 이와 유사한 사례를 python으로 명사만 추출하는 것을 얼마전 python korea 2017에서 발표하신것을 봤는데 한번 참고해보시는 것은 어떨까요? url 적어 드립니다. 감사합니다.
      URL : https://www.slideshare.net/kimhyunjoonglovit/pycon2017-koreannlp

  16. blue1blue 2017.09.11 09:27

    안녕하세요. 파이썬 관련 자료를 공유해주셔서 감사합니다.
    제가 알기론 현재 R로서는 동일한 "주어"나 "유사어"는 묶어주고 단일 키워드(ex) 을, 를)는 클리닝 작업을 해서 분석을 하는걸로 아는데 그 이외의 R로 분석하면서 좋은 방법을 아시면 공유 부탁 드립니다.

    감사합니다.

  17. 김지현 2017.10.25 23:57

    좋은 자료 정말 정말 감사합니다!!! 공부가 많이 되었습니다^0^

  18. 지니 2018.03.11 22:02

    소스를 그대로 실행했는데 에러가 납니다. 우선 xml2 패키지를 설치하니 " C:/Users/lkyun/Documents/R/win-library/3.4’의 위치에 패키지(들)을 설치합니다.
    (왜냐하면 ‘lib’가 지정되지 않았기때문입니다" 이런 메세지가 나온후 library(xxxml2) 하니 xml2라고 불리는 패키지가 없다고 나오는데요 무엇이 문제인지요?

    • BlogIcon 쿡북 2018.03.12 17:09 신고

      해당 오류는 라이브러리가 정상적으로 설치되지 않아서 생긴 것으로 보입니다. 패키지 설치쪽을 따로 검색하셔서 보셔야 할 것 같아요. 감사합니다.

  19. 익명 2018.04.18 17:31

    비밀댓글입니다

  20. 몽상가 2018.07.17 18:11

    좋은 정보 얻어갑니다.
    수정해 주신 코드로도 TermDocumentMatrix() 이후 인코딩 문제로 한글이 깨지는 것을 보완할 수 없어 구글 검색을 통해 확인 한 내용을 공유합니다. 수고하세요.

    *출처 : https://www.facebook.com/groups/krstudy/permalink/765747536932854/

    #=========== 내용 ==========
    작성하신 코드에서 Corpus 함수를 쓰셨는데, 이 함수로 생성한 "SimpleCorpus" 문제 때문입니다. tm 패키지 매뉴얼에서 TermDocumentMatrix 함수설명 가운데, "control" argument 부분을 옮겨보겠습니다.

    "This is different for a SimpleCorpus. In this case all options are processed in a fixed order in one pass to improve performance. It always uses the Boost(http://www.boost.org) Tokenizer (via Rcpp) and takes no custom functions as option arguments."

    SimpleCorpus에 대해서는 tokenizer로 "언제나" Boost란 녀석을 쓴다고 되어 있네요.

    따라서 작성하신 코드에서 Corpus를 VCorpus로 바꾸고, as.matrix(tdm.ja) 해보세요. 그럼 깨지지 않고 매트릭스가 나오는 것을 확인하실 수 있을 겁니다.

    • BlogIcon 쿡북 2018.07.18 00:38 신고

      감사합니다. 포스팅이 오래전 글이라 여러 업데이트를 하지 못했는데 읽으시는 분들이 많은 도움 되실것 같네요. 의견 감사합니다. 주신의견은 포스팅에 포함하겠습니다

  21. dong 2018.08.20 23:20

    안녕하세요 예전 게시물임에도 불구하고 혹시나 해서 질문을 드립니다!

    결과가 다 잘나오는데, 한가지 plot의 화질이 현저하게 떨어집니다 ㅠㅠ
    저는 R에 plot탭에서 export를 클릭하여 사용했는데, 글쓴이님처럼 선명하지 않고
    node의 둘레가 우글우글 화질이 깨집니다..혹시 글쓴이님은 어떻게 plot을 저장하셨나요?

    감사합니다 :)