matsulibの日記

Ingredients as Code

Pythonで2chのスレをスクレイピングしてMeCabで形態素解析してみる

スクレイピングはlxmlが便利。
MeCabWindows用のバイナリが用意されてるけど、それだとpythonから使うためのmecab-pythonがインストールできないらしく、MeCabソースコードを弄って自分でビルドしなくちゃいけないらしい。
ということでいくつかの記事を読んで試したけど結局うまく行かず心が折れてしまった。
一方、Ubuntu(実際使ったのはLinuxMint)ならソースコードを弄る必要はなかった。

参考サイト
Ubuntu 13.10でRubyからMeCabを使えるようにしたメモ - Qiita
python3対応 Mecabの紹介 - Python, web, Algorithm 技術的なメモ
PHP - 2chスレをスクレイピングする方法 - Qiita

さて、2chスレのURLを与えて、その中のIDと単語をカウントしてみた。
ただしURLや顔文字が分解されて出てきたような記号、その他無意味な文字列は除外するようにした。
以下、コードと結果。

from lxml.html import fromstring
from urllib.request import urlopen
from collections import Counter, defaultdict
import MeCab

mecab = MeCab.Tagger()
url = 'http://wc2014.2ch.net/test/read.cgi/sci/1343188288/'
doc = fromstring(urlopen(url).read().decode('cp932', errors='ignore'))


# タイトル
print(doc.head.find('title').text, url, sep='\n')
print()

# IDのカウント
ids = Counter([data.text_content().split('ID:')[-1].split()[0] for data in doc.findall('.//dt')])
print('全レス数:%d, 全ID数:%d' % (len(list(doc.findall('.//dd'))), len(ids)))
for i, id in enumerate(sorted(ids.items(), key=lambda x:x[1], reverse=True)[:10]):
    print('{0:2d}. ID:{1:15s} ({2:3d} 回)'.format(i+1, id[0], id[1]))
print()

# 頻出ワードのカウント
word_count = defaultdict(int)
is_kanji = lambda c: True if '一' <= c <= '龠' else False
ignore_str0 = "ch,彡ノシ丶゙゙ミll丿,あと,こと,ない"  # ある文字列がこの中に完全に含まれたら無視する
ignore_list2 = ['接尾', '数', 'サ変接続', '代名詞', '非自立']  # 無視する品詞の種類
for comment in doc.findall('.//dd'):
    text = comment.text_content()
    # URLの除去
    for link in comment.iterlinks():
        text = text.replace(link[0].text_content(), '')

    result = mecab.parse(text)
    words = [word.replace('\t',',').split(',') for word in result.split('\n')[:-2]]
    for word in words:
        if word[1] in ['名詞', '形容詞']:
            # 一文字のワードは漢字だけカウントする
            if len(word[0])==1 and not is_kanji(word[0]):
                continue
            if word[0] in ignore_str0 or word[2] in ignore_list2:
                continue
            # print(word)
            word_count[word[0]] += 1

    
for i, word in enumerate(sorted(word_count.items(), key=lambda x:x[1], reverse=True)[:30]):
    print('{0:2d}: {1:s} ({2:d})'.format(i+1, word[0], word[1]), end=(' ' if (i+1)%3 else '\n'))


# ヒッグス粒子とは何かを語るスレⅢ
# http://wc2014.2ch.net/test/read.cgi/sci/1343188288/
# 
# 全レス数:947, 全ID数:159
#  1. ID:???             (714 回)
#  2. ID:+4bdp0cd        (  7 回)
#  3. ID:DLFkhGxw        (  7 回)
#  4. ID:kUvLjV7c        (  6 回)
#  5. ID:vUMHeQOf        (  4 回)
#  6. ID:ODpd2QRq        (  4 回)
#  7. ID:68+kverR        (  4 回)
#  8. ID:qodb61LD        (  3 回)
#  9. ID:ECkbWvJ2        (  3 回)
# 10. ID:7huuyKeL        (  3 回)
# 
#  1: ヒッグス (488)  2: 粒子 (458)  3: 質量 (295)
#  4: 理論 (169)  5: 重力 (126)  6: 素粒子 (117)
#  7: エネルギー (99)  8: 宇宙 (92)  9: 物理 (87)
# 10: モデル (78) 11: 対称 (77) 12: 物質 (71)
# 13: 標準 (50) 14: 光速 (47) 15: 真空 (42)
# 16: 量子 (41) 17: スレ (40) 18: 空間 (40)
# 19: LHC (40) 20: 電子 (38) 21: 相互 (38)
# 22: 相対 (36) 23: 状態 (36) 24: GeV (35)
# 25: 可能 (34) 26: ゲージ (31) 27: 無い (29)
# 28: 光 (28) 29: 時間 (28) 30: エーテル (28)

これは色々応用できそう。あとは正規表現を使えるようになりたい。

新しい辞書を使う 2015/03/14 追記

MeCab用の素晴らしい新語辞書が公開されました。

[O] MeCab 用の新語辞書 mecab-ipadic-neologd を公開しました

さっそくインストールして使ってみる。使い方は MeCab.Tagger() の引数に辞書を指定するオプションを文字列として与える。上のスレの形態素解析の結果はこうなる。

mecab = MeCab.Tagger('-d /usr/lib/mecab/dic/mecab-ipadic-neologd')
#  1: 質量 (290)  2: ヒッグス粒子 (277)  3: 粒子 (148)
#  4: ヒッグス場 (128)  5: 素粒子 (116)  6: 理論 (105)
#  7: エネルギー (89)  8: 重力 (83)  9: 宇宙 (75)
# 10: ヒッグス (74) 11: 物質 (60) 12: モデル (51)
# 13: 光速 (46) 14: 物理 (46) 15: LHC (41)
# 16: スレ (40) 17: 電子 (38) 18: GeV (35)
# 19: 空間 (33) 20: 相対論 (33) 21: 状態 (32)
# 22: 対称性 (31) 23: 10 (29) 24: 無い (29)
# 25: 真空 (29) 26: 物理学 (29) 27: 相互作用 (28)
# 28: 理由 (28) 29: エーテル (28) 30: 結果 (27)

うん、良い感じ。(数は除いてるはずだが何故「10」が……?)

固有名詞にすごく強い。

import MeCab

text = '魔法少女まどか☆マギカ、略してまどマギ。'

mecab_old = MeCab.Tagger()
mecab_new = MeCab.Tagger('-d /usr/lib/mecab/dic/mecab-ipadic-neologd')

print(mecab_old.parse(text))
# 魔法	名詞,一般,*,*,*,*,魔法,マホウ,マホー
# 少女	名詞,一般,*,*,*,*,少女,ショウジョ,ショージョ
# まどか	名詞,固有名詞,人名,名,*,*,まどか,マドカ,マドカ
# ☆	記号,一般,*,*,*,*,☆,☆,☆
# マギカ	名詞,固有名詞,組織,*,*,*,*
# 、	記号,読点,*,*,*,*,、,、,、
# 略し	動詞,自立,*,*,五段・サ行,連用形,略す,リャクシ,リャクシ
# て	助詞,接続助詞,*,*,*,*,て,テ,テ
# ま	フィラー,*,*,*,*,*,ま,マ,マ
# ど	接頭詞,名詞接続,*,*,*,*,ど,ド,ド
# マギ	名詞,一般,*,*,*,*,*
# 。	記号,句点,*,*,*,*,。,。,。
# EOS

print(mecab_new.parse(text))
# 魔法少女まどか☆マギカ	名詞,固有名詞,一般,*,*,*,魔法少女まどか☆マギカ,マホウショウジョマドカマギカ,マホウショウジョマドカマギカ
# 、	記号,読点,*,*,*,*,、,、,、
# 略し	動詞,自立,*,*,五段・サ行,連用形,略す,リャクシ,リャクシ
# て	助詞,接続助詞,*,*,*,*,て,テ,テ
# まどマギ	名詞,固有名詞,一般,*,*,*,まどマギ,マドマギ,マドマギ
# 。	記号,句点,*,*,*,*,。,。,。
# EOS