programming/Python

나무위키 최근 변경 페이지 키워드 분석하기_데이터마이닝 2탄

Jofresh 2023. 7. 2. 14:39
728x90
반응형

이제 분석에 사용할 데이터가 준비되었으니, 본격적으로 텍스트 마이닝을 알아봅시다.

 

** 분석에 사용할 데이터는 이전 강의 참조해주세요.

 

[데이터마이닝_1강] 웹 크롤링으로 기초 데이터 수집하기

이번 데이터마이닝 강의에서는 웹 크롤링으로 데이터를 수집하고, 키워드를 추출하여, 키워드 간의 연관 관계를 분석하고 이 결과를 시각화 해보도록 하겠습니다. 웹 크롤링으로 데이터 수집하

jofresh.tistory.com

 

 

크롤링: 웹 데이터 가져오기

이전 단계와 동일한 방법으로 웹 데이터를 크롤링합니다. 단, 이번에는 모든 URL의 데이터를 가져와 볼게요.

다음 코드를 실행하여 나무위키에서 최근 변경이 일어난 페이지들의 URL을 page_urls라는 변수에 저장합니다.

 

# 크롤링한 데이터를 데이터 프레임으로 만들기 위해 준비합니다.
columns = ["title", "category", "content_text"]
df = pd.DataFrame(columns=columns)

# 각 페이지별 '제목', '카테고리', '본문' 정보를 데이터 프레임으로 만듭니다.
for page_url in page_urls:

    # 사이트의 html 구조에 기반하여 크롤링을 수행합니다.
    # driver = webdriver.Chrome(path)  # for Mac
    driver = webdriver.Chrome(executable_path=excutable_path)  # for Windows
    driver.get(page_url)
    req = driver.page_source
    soup = BeautifulSoup(req, "html.parser")
    contents_table = soup.find(name="article")
    title = contents_table.find_all("h1")[0]
    
    # 카테고리 정보가 없는 경우를 확인합니다.
    if len(contents_table.find_all("ul")) > 0:
        category = contents_table.find_all("ul")[0]
    else:
        category = None
        
    content_paragraphs = contents_table.find_all(name="div", attrs={"class":"wiki-paragraph"})
    content_corpus_list = []
    
    # 페이지 내 제목 정보에서 개행 문자를 제거한 뒤 추출합니다. 만약 없는 경우, 빈 문자열로 대체합니다.
    if title is not None:
        row_title = title.text.replace("\n", " ")
    else:
        row_title = ""
    
    # 페이지 내 본문 정보에서 개행 문자를 제거한 뒤 추출합니다. 만약 없는 경우, 빈 문자열로 대체합니다.
    if content_paragraphs is not None:
        for paragraphs in content_paragraphs:
            if paragraphs is not None:
                content_corpus_list.append(paragraphs.text.replace("\n", " "))
            else:
                content_corpus_list.append("")
    else:
        content_corpus_list.append("")
        
    # 페이지 내 카테고리정보에서 “분류”라는 단어와 개행 문자를 제거한 뒤 추출합니다. 만약 없는 경우, 빈 문자열로 대체합니다.
    if category is not None:
        row_category = category.text.replace("\n", " ")
    else:
        row_category = ""
    
    # 모든 정보를 하나의 데이터 프레임에 저장합니다.
    row = [row_title, row_category, "".join(content_corpus_list)]
    series = pd.Series(row, index=df.columns)
    df = df.append(series, ignore_index=True)
    
    # 크롤링에 사용한 브라우저를 종료합니다.
    driver.close()
    
    # 데이터 프레임을 출력합니다.
df.head(5)

 

이제 이 주소들에 다시 한번 접근하여 문서의 본문과 제목, 그리고 카테고리에 등장하는 텍스트 데이터를 가져와봅시다.

 

 

실행결과

위의 실행 결과는 모든 URL의 텍스트 데이터를 가져온 뒤, 이를 데이터 프레임의 형태로 변환한 것입니다. 데이터에 등장하는 불필요한 문자인 '\n','분류'는 크롤링 과정에서 replace()함수로 제거했습니다.

 

추출: 키워드 정보 추출하기

다음은 수집한 텍스트 데이터에서 키워드 정보를 추출하는 단계입니다. 이를 위해 텍스트 전처리 작업이 필요합니다.

텍스트 전처리는 특수문자나 외국어를 제거하는 등의 과정을 포함합니다. 그런데 이는 언어와 상황 마다 다를 수 있습니다.

 

예를 들어 스팸메일을 분류하는 텍스트 마이닝의 경우, 특수문자나 외국어가 분석의 중요한 힌트가 될 수 있기 때문에 이를 제거하지 않는편입니다. 반면, 키워드 분석처럼 '단어'를 추출하는 것이 목적이라면 특정 언어의 글자만을 추출하기도 합니다.

 

파이썬에서는 're'라는 모듈을 통해 정규포현식을 사용할 수 있습니다. 정규표현식이란 특정한 규칙을 가진 문자열의 집합을 표현하는 형식입니다. 만약 다음 코드와 같이 

 

re.compile(''^ ㄱ-ㅣ가-힣]+')이라는 코드로 한글에 대한 정규표현식을 정의하면 대상이 되는 텍스트 데이터에서 한글만 추출할 수 있게 됩니다.

 

# 텍스트 정제 함수 : 한글 이외의 문자는 전부 제거합니다.
def text_cleaning(text):
    hangul = re.compile('[^ ㄱ-ㅣ가-힣]+') # 한글의 정규표현식을 나타냅니다.
    result = hangul.sub('', text)
    return result

실행 결과

모든 데이터에 전처리를 적용하기 위해서는 apply()함수를 사용합니다. 다음 코드는 title,category,content_text 3개의 피처에 apply()함수를 적용한 것입니다. 이를 head()함수로 출력하면 한글을 제외한 문자들이 제거됩니다.

# 각 피처마다 데이터 전처리를 적용합니다.
df['title'] = df['title'].apply(lambda x: text_cleaning(x))
df['category'] = df['category'].apply(lambda x: text_cleaning(x))
df['content_text'] = df['content_text'].apply(lambda x: text_cleaning(x))
df.head(5)

실행결과

 

다음 과정은 키워드를 추출한 뒤, 빈도 분석을 수행하는 과정입니다. 

키워드 추출이란 좁은 의미에서는 명사, 혹은 형태소 단위의 문자열을 추출하는 것입니다. 이를 수행하기 위해 말뭉치라는 것을 만들어야 합니다.

 

말뭉치는 말 그대로 텍스트 데이터의 뭉텅이를 의미합니다. 이번 예제에서는 제목 단위, 카테고리 단위, 본문 단위의 키워드를 분석하기 위해 제목 말뭉치, 카테고리 말뭉치, 본문 말뭉치 총 3개의 말뭉치를 생성합니다.

 

다음 코드에서는 텍스트 피처를 tolist()로 추출한 뒤, join() 함수로 말뭉치를 생성해주었습니다.

# 각 피처마다 말뭉치를 생성합니다.
title_corpus = "".join(df['title'].tolist())
category_corpus = "".join(df['category'].tolist())
content_corpus = "".join(df['content_text'].tolist())
print(title_corpus)

실행 결과

이제 각 말뭉치 안에서 등장하는 형태소를 추출하겠습니다.

파이썬의 한국어 형태소 추출 라이브러리중 가장 쉽게 사용할 수 있는 konlpy를 사용합니다.

pip install konlpy

 

자 그럼 konlpy를 이용해서 형태소를 추출해보겠습니다.

 

from konlpy.tag import Okt
from collections import Counter

# konlpy의 형태소 분석기로 명사 단위의 키워드를 추출합니다.
nouns_tagger = Okt()
nouns = nouns_tagger.nouns(content_corpus)
count = Counter(nouns)
count

실행 결과

출력 결과를 보면 의미없는 키워드 들이 발견됩니다. 이런 키워드를 처리하기 위해서는 우선 한 글자 키워드를 제거합니다.

특정한 의미를 가지는 한 글자 키워드는 따로 예외 처리를 해주는 것이 좋지만 이번에는 그런 과정은 생략할께요.

 

한 글자 키워드 제거하는 코드

# 한글자 키워드를 제거합니다.
remove_char_counter = Counter({x : count[x] for x in count if len(x) > 1})
print(remove_char_counter)

실행 결과

이제 실질적인 의미가 없는 키워드를 처리합니다. '입니다.'그', '저' 등과 같은 접속사와 같은 품사는 지우도록 할께요.

 

이단계에서 필요한 것이 불용어 사전입니다.다음 코드의 stopwords가 한국어의 약식 불용어 사전 예시입니다. 이 불용어 사전에는 '아','휴'처럼 키워드로서의 의미를 가지지 않는 형태소들이 포함되어 있습니다.

 

불용어 삭제 코드

 

다음으로 나무위키 페이지에 맞는 불용어를 추가로 제거합니다.

 

# 나무위키 페이지에 맞는 불용어를 추가합니다.
namu_wiki_stopwords = ['상위', '문서', '내용', '누설', '아래', '해당', '설명', '표기', '추가', '모든', '사용', '매우', '가장',
                       '줄거리', '요소', '상황', '편집', '틀', '경우', '때문', '모습', '정도', '이후', '사실', '생각', '인물', 
                       '이름', '년월']
for stopword in namu_wiki_stopwords:
    stopwords.append(stopword)
    
    # 키워드 데이터에서 불용어를 제거합니다.
remove_char_counter = Counter({x : remove_char_counter[x] for x in count if x not in stopwords})
print(remove_char_counter)

 

실행결과

 

 

 

시각화: 워드 클라우드 시각화 하기

지금까지 분석한 키워드를 시각화 합니다.

워드 클라우드 기법으로 시각화 해보도록 하겠습니다.

 

그럼 워드 클라우드 기법을 제공하는 패키지를 먼저 설치해야 합니다.

pip install pytagcloud pygame simplejson

 

그러면 pytagcloud를 사용하여 키워드 정보 객체로부터 가장 빈도수가 높은 n개의 키워드를 추출해보겠습니다.

 

다음 코드에서는 빈도수를 40개로 설정, 디자인 관련 파라미터인 maxsize는 80으로 설정하였습니다. 

그리고 이미지를 바로 출력하기 위해 image 클래스를 사용합니다.

 

제목 키워드 코드 입니다.

import random
import pytagcloud
import webbrowser

# 가장 출현 빈도수가 높은 40개의 단어를 선정합니다.
ranked_tags = remove_char_counter.most_common(40)

# pytagcloud로 출력할 40개의 단어를 입력합니다. 단어 출력의 최대 크기는 80으로 제한합니다.
taglist = pytagcloud.make_tags(ranked_tags, maxsize=80)

# pytagcloud 이미지를 생성합니다. 폰트는 나눔 고딕을 사용합니다.
pytagcloud.create_tag_image(taglist, 'wordcloud.jpg', size=(900, 600), fontname='NanumGothic', rectangular=False)

# 생성한 이미지를 주피터 노트북상에서 출력합니다.
from IPython.display import Image
Image(filename='wordcloud.jpg')

nouns_tagger = Okt()
nouns = nouns_tagger.nouns(title_corpus)
count = Counter(nouns)

remove_char_counter = Counter({x : count[x] for x in count if len(x) > 1})
remove_char_counter = Counter({x : remove_char_counter[x] for x in count if x not in stopwords})

ranked_tags = remove_char_counter.most_common(40)
taglist = pytagcloud.make_tags(ranked_tags, maxsize=80)
pytagcloud.create_tag_image(taglist, 'title_wordcloud.jpg', size=(900, 600), fontname='NanumGothic', rectangular=False)

Image(filename='title_wordcloud.jpg')

 

실행 결과

제목 키워드 실행 결과

 

카테고리 키워드 코드 입니다.

 

nouns_tagger = Okt()
nouns = nouns_tagger.nouns(category_corpus)
count = Counter(nouns)

remove_char_counter = Counter({x : count[x] for x in count if len(x) > 1})
remove_char_counter = Counter({x : remove_char_counter[x] for x in count if x not in stopwords})

ranked_tags = remove_char_counter.most_common(40)
taglist = pytagcloud.make_tags(ranked_tags, maxsize=80)
pytagcloud.create_tag_image(taglist, 'category_wordcloud.jpg', size=(900, 600), fontname='NanumGothic', rectangular=False)

Image(filename='category_wordcloud.jpg')

실행 결과

 

카테고리 키워드 실행 결과

 

 

꽤 복잡하고 불용어 텍스트를 지우는 과정에서 사람의 손의 필요했었네요. 공부에 도움이 되셨으면 합니다!

그럼 20000!

728x90
반응형