目次

HanDicをPythonで利用する:TF-IDFの計算

はじめに

HanDicをPythonで利用する例として,文書中に現れる単語の重要度を示す,TF-IDFの計算を行ってみます.TF-IDFについては以下などを参照:

実際にスクリプトを書くに当たっては,以下を参考にしました.必要な部分をだいたい丸写ししています:

Pythonのバージョンは3.7.11,HanDicはhandic-mecab-20210116を用いました.MeCabを扱うためのモジュールmecab-python3ほか,必要なモジュールがインストールされていることが前提です.HanDicはPythonスクリプトのあるディレクトリ下,handicという名前のディレクトリに保存されているとします.

なお,HanDicに含まれるファイルのうちdicrcに記述の不備があり,「bos-feature」行の末尾に「,*,*」を追加しました.

また,HanDicをPythonで利用するで説明したk2jamo.pyも作っておきます.

やること

韓国の新聞社サイトで公開されているRSSフィードを使って,リストにある記事を取得し,名詞類(普通名詞,固有名詞,語根)のTF-IDFを計算します.その上で,リスト中の記事から10本をランダムに選び,TF-IDF上位10語を列挙します.

ここでは例として,경향신문(京郷新聞)のRSSサービスから,「정치(政治)」カテゴリのフィードを利用します.大手の新聞社でRSSサービスを行っているのは以下の通りです:

付記した理由から,ここでは경향신문を取り上げることにします.他のRSSを利用する場合,適宜読み替えてください.

準備

必要なモジュールなどを呼び出します.

import random, requests, MeCab
from bs4 import BeautifulSoup
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
import k2jamo

何をどういう順番で呼び出すのか,お作法がちょっと分からないので適当にやっています.

MeCabの呼び出し

MeCabのインスタンスを作成します.

tokenizer = MeCab.Tagger('-d handic')
tokenizer.parse('')

記事のスクレイピング

경향신문の記事URLから該当する記事を取得し,本文(<p class='content_text'>の内容)を取得します.requests.get()でうまく記事が取得できなかったので,User Agentを設定してあります.

# 記事と本文の取得
def news_scraping(url):
    paras = []
    ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    headers = {'User-Agent': ua}
    html = requests.get(url, headers=headers)
    soup = BeautifulSoup(html.content, 'html.parser')
    # 경향신문向け
    articles = soup.select('p.content_text')
    # 중앙일보向け
    # articles = soup.select('#article_body > p')
    for p in articles:
        paras.append(p.text)
    return '\n'.join(paras)

なお,記事本文が格納されている要素はサイトによって異なります.ここまで分かっている中では,중앙일보(中央日報)の場合,「경향신문向け」としてある次の行をコメントアウトして,articles = soup.select('#article_body > p')の方を使用すると,記事本文が取り出せます.他はまだ調査中です.

[2021/11/06 追加] いくつか調べた結果,書き換えの内容は以下の通りです.

한겨레(ハンギョレ) articles = soup.select('div.article-text')
노컷뉴스(ノーカットニュース・CBS) articles = soup.select('div', id = 'pnlContent')

MeCabによる解析

MeCabで解析し,普通名詞(世宗タグ「NNG」),固有名詞(同「NNP」),語根(同「XR」)を取り出してその基本形を配列に格納します.最後に,配列の内容をスペースで連結します.

# mecabで解析,スペース区切りの配列にする
def extract(text):
    words = []
    node = tokenizer.parseToNode(text)
    while node:
        if node.feature.split(',')[10] in ['NNG', 'NNP', 'XR']:
            words.append(node.feature.split(',')[5])
        node = node.next
    text_result = ' '.join(words)
    return text_result

名詞だけでもよいのですが,語根には「간단02(簡単)」「깨끗」「소중01(所重)」などといった語があり,特に政治の記事では人や政策のイメージなどとも関連すると思われるので,とりあえず含めてあります.世宗タグについてはHanDicの品詞体系(すいません,書きかけです)を参照のこと.

RSSフィードからのリンク取得および記事の処理

RSSフィードから各記事のリンクを取得します.

# RSSフィードの処理
# 경향신문 정치
feed = 'https://www.khan.co.kr/rss/rssdata/politic_news.xml'
# 중앙일보 주요기사
# feed = 'https://rss.joins.com/joins_homenews_list.xml'
req = requests.get(feed)
req.encoding = req.apparent_encoding
txt = BeautifulSoup(req.text, 'lxml-xml')
rss_list = txt.select('channel > item')

中央日報の主要記事も例として挙げてあります.中央日報で試す場合はコメントアウトを外し,上の「경향신문 정치」の次の行をコメントアウトしてください.また上述の通り,news_scraping()も変更しておく必要があります.

次に,リンクのリストからそれぞれの記事を取得して処理し,MeCabで解析する部分です.

# 記事本文の処理,docsに格納
docs = []
titles = []
for i in range(len(rss_list)):
    print("processing:", i+1, "/", len(rss_list))
    text = k2jamo.substitute(news_scraping(rss_list[i].link.text))
    text = extract(text)
    titles.append(rss_list[i].title.text)
    docs.append(text)

各記事のタイトルは,別途配列に入れました.

TF-IDFの計算と上位10語の表示

scikit-learnでTF-IDFを計算します.アップデートしたら「get_feature_names()はバージョン1.0では非推奨で,1.2で削除する」と出てきたので,get_feature_names_out()を使っています.

# モデルを作成
vectorizer = TfidfVectorizer(smooth_idf=False)
values = vectorizer.fit_transform(docs).toarray()
feature_names = vectorizer.get_feature_names_out()
df = pd.DataFrame(values, columns = feature_names, index=titles)

上記データフレームから,ランダムに10件の記事を選んで,それぞれTF-IDFの値で上位10語を列挙します.

# 文書をランダムに10個選択,それぞれ上位10語の出力
for num in random.sample(range(len(df)), 10):
    temp_df = []
    temp_df = df[num:num+1].T
    temp_df = temp_df.sort_values(by=titles[num], ascending=False)
    print(temp_df.head(10))

実行結果は以下の通り.フィードの項目(item)数が10以下だとエラーになります.random.sample()で取り出す数を変更してください.

> python handic_mecab.py
processing: 1 / 54
Some characters could not be decoded, and were replaced with REPLACEMENT CHARACTER.
...中略...
processing: 54 / 54
Some characters could not be decoded, and were replaced with REPLACEMENT CHARACTER.
      안철수 출마와 김동연, 제3지대 가능성은
지대07                0.363606
동연                  0.293522
철수                  0.269679
안03                 0.248124
후보04                0.186889
양보03                0.149720
철수02                0.146761
사람                  0.141769
출마                  0.135213
김06                 0.127492
      한미연합군사령관 “가장 강력하고 준비된 연합전력”
연합03                     0.383949
연합사                      0.358117
군사령부                     0.268588
주한02                     0.26858814                      0.239796
창설                       0.179059
억제                       0.179059
기념식                      0.179059
미구03                     0.179059
유지09                     0.098292
      윤석열, 공정·정의·소통 강조하며 ‘반문 기치’…“내 승리, 뼈아플 것”
윤01                                   0.297543
총장01                                  0.247608
정권04                                  0.226711
연설02                                  0.201548
나라01                                  0.180365
다짐                                    0.179952
공정02                                  0.172756
상식06                                  0.157493
후보04                                  0.152771
약탈                                    0.140487
      당심에 막힌 홍준표의 두 번째 도전…“경선서 국민 관심 끌어온 게 내 역할”
홍01                                     0.288711
의원05                                    0.250445
경선08                                    0.197312
도전04                                    0.187441
역할                                      0.182526
세대02                                    0.180847
윤01                                     0.149584
마지막                                     0.145998
총장01                                    0.136150
중순                                      0.132053
      민주당, 윤석열 후보 확정에 의혹·실언 거론하며 “정책·비전 선거” 제안
후보04                                  0.315293
의혹                                    0.280119
정책02                                  0.203595
수사18                                  0.203595
민주당                                   0.187165
윤01                                   0.185772
축하                                    0.175173
비전06                                  0.175173
중심01                                  0.155191
대변인                                   0.144918
      문 대통령, 체코 등 4개국과 정상회담서 ‘원전 협력’ 강조…탈원전 정책 역행
원전04                                     0.623669
체코                                       0.205854
문01                                      0.178851
폴란드                                      0.176447
회담                                       0.176447
정상11                                     0.157953
총리01                                     0.155917
대통령                                      0.154182
기술01                                     0.147039
헝가리                                      0.147039
      유승민 “깨끗이 승복하겠다”···백의종군 선언
경선08                   0.338128
승리                     0.330845
패배                     0.317707
의원05                   0.268237
지지자                    0.211805
승복02                   0.166795
문05                    0.158157
결과02                   0.157122
일01                    0.154956
대선80                   0.150343
      이재명 “대선이 정책과 비전으로 미래 열어가는 장이 되길” 정의당 “고발 사주와 대장동…누가 덜 나쁜지 경쟁은 안 돼”
후보04                                           0.364681
축하                                             0.334874
윤01                                            0.248031
대변인                                            0.211075
미래02                                           0.183283
대선80                                           0.178533
수석02                                           0.158307
대출03                                           0.158307
의혹                                             0.152999
수사18                                           0.148270
      청와대 ‘요소수 대응 TF’ 가동··· 매일 비상점검
요소04                       0.403954
운영03                       0.315363
대응02                       0.297735
외교01                       0.242372
수급02                       0.188388
점검                         0.188388
수석02                       0.178641
수석                         0.161582
청와대                        0.155120
체계03                       0.145901
      송영길 “윤석열, 국민과 언론 앞에 성실하게 답변하라”
송01                         0.438483
성실02                        0.255614
의혹                          0.234262
수사18                        0.227021
기대03                        0.219745
답변                          0.219242
대표                          0.191708
후보04                        0.186125
윤01                         0.172622
경쟁                          0.170266

文字コード関連のエラーが出ますが,だいたいうまくいっているようです.경향신문以外の記事を使う場合,このエラーは出ませんでした.

大統領選挙(대선)が近づいて,各党の候補者選び(경선)が熱を帯びていることもあり,それぞれの記事で取り扱われている候補者(후보)の名前(윤, 홍, 유, 안, 김, …)などが上位にきていることが分かります.

終わりに

RとRMeCabで扱う場合,別途Perlなどで字母に分解したりする必要がありましたが,データの取得から文字列の処理,形態素解析,計算まで一つの言語で完結するのは便利です.