Wikipedia見出しリストから不要な見出しをカットする

検索サイトで「wikipedia mecab」と検索するとWikipediaの見出しリストを整形してMeCab用辞書として使う記事が何件かhitします。
しかし、Wikipediaの見出しは形態素解析用に作られたわけではないので、形態素として扱うと不都合になることがあります。
たとえば、文書から名詞だけを抽出して文書をカテゴリに分類したいといったときに、「ロシアの音楽」という文字列を「ロシアの音楽/名詞」と解釈するよりも「ロシア/名詞 の/助詞 音楽/名詞」と解釈したほうが汎化できるので嬉しいです。

そのため不要な見出しを除去する処理を行うことにします。
まず、見出しリストをざっと見て「<国名>の法律」「<歌手>のディスコグラフィ」などの「AのB」パターンが多いことに着目しました。
そこで、AのBパターンをざっくりと抽出します。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
from collections import Counter
import re
 
suffixes = Counter()
re_anob = re.compile(u'(?P<A>.+[^の])の(?P<B>[^の].+)')
re_hiragana = re.compile(u'[ぁ-&#12438;]+')
 
def extract_anob(text):
    match = re_anob.match(text)
    if match and is_valid_b(match.group('B')):
        suffixes[match.group('B')] += 1
 
def is_valid_entry(entry):
    return False if '_(' in entry else True
 
def is_valid_b(b):
    return False if re_hiragana.match(b) else True
 
if __name__ == '__main__':
    import optparse
 
    parser = optparse.OptionParser()
    parser.add_option("-f", "--filename", help="the path of input file")
    (opts, args) = parser.parse_args()
 
    for line in open(opts.filename, 'r'):
        line = line.strip().decode('utf8')
        if is_valid_entry(line):
            extract_anob(line)
    for k,v in suffixes.most_common(200):
        print k,v

AのBパターンのBの出現回数上位200件がこちら。

一覧 2256
戦い 1581
人物一覧 596
登場人物 467
歴史 452
法則 322
国旗 280
国歌 261
定理 256
国章 238
スポーツ 221
作品 199
通貨 198
地方行政区画 157
鉄道 156
音楽 153
大統領 150
世界遺産 149
軍事 146
冒険 145
廃校一覧 145
オールナイトニッポン 143
選手一覧 140
行政区画 131
航空 128
物語 125
映画 120
政治 120
同位体 117
野球 116
首相 116
政党 114
メジャーリーグベースボール 113
ワールドシリーズ 112
楽曲一覧 111
国会 110
海戦 101
宝塚歌劇公演一覧 99
ディスコグラフィ 93
日本公開映画 90
年度別成績一覧 90
世界 88
手紙 86
MLBオールスターゲーム 81
関係 80
経済 79
交通 79
逆襲 78
大冒険 77
政党一覧 77
ゲームタイトル一覧 77
年表 77
連続ドラマ 76
伝説 75
時間 75
バレーボール 73
原理 73
エピソード一覧 72
歴代選手・スタッフ一覧 71
式内社一覧 69
反乱 69
アニメ 68
奇跡 66
F1世界選手権 65
ロードレース世界選手権 65
日本シリーズ 64
ユニフォーム 64
パラドックス 61
NBA 61
教育 61
登場人物一覧 61
権利 60
恋人 59
秘密 58
合戦 56
事件簿 56
女性史 54
郡一覧 52
虐殺 52
料理 52
高等学校一覧 52
ユーロビジョン・ソング・コンテスト 52
資格者配置路線 52
アメリカ合衆国上院議員 51
国鉄 50
有名人一覧 50
廃止市町村一覧 50
MLBドラフト 49
空軍 49
悲劇 48
廃園一覧 48
NFL 48
原則 47
登場キャラクター 47
ナショナルリーグチャンピオンシップシリーズ 46
肖像 46
市町村章一覧 46
野望 46
地理 45
文化 45
アメリカンリーグチャンピオンシップシリーズ 45
県道一覧 45
騎士 44
日々 43
文学 42
テーマ 41
世界ラリー選手権 41
誓い 41
公式 41
大学一覧 41
ニュース 40
灯台一覧 40
海軍 39
二級水系一覧 39
比較 39
日本競馬 38
方程式 37
国・地域別メダル受賞数一覧 37
王様 37
少女 37
時代 37
博物館 36
季節 36
オールナイトニッポンR 36
野鳥一覧 36
町名 36
中で 35
陸軍 35
自由 35
天使 34
観光地 34
カメラ製品一覧 34
人々 34
女王 33
議会 33
森公園 33
殺人 33
和約 33
科学 32
自転車競技 32
チーム記録 32
警察 31
陰謀 31
大統領一覧 31
部屋 31
歴代監督一覧 31
祈り 31
下で 30
促進に関する法律 30
問題 30
花嫁 30
政変 30
日本 30
NBAドラフト 30
サッカー選手一覧 30
約束 29
迷宮 29
記憶 29
リスト 28
韓国シリーズ 28
競馬 28
呪い 28
宇宙飛行 28
元素 28
相撲 28
ラジオ 27
事件 27
JSL 27
図書館一覧 27
聖母 27
スーパーバイク世界選手権 26
ワイン 26
渡し 26
作品一覧 26
神話 26
日記 26
青春 26
理論 26
用語一覧 26
宗教 25
女神 25
挑戦 25
憲法 25
ユーロ硬貨 25
電車 25
秘宝 24
殿堂 24
戦争 24
企業一覧 24
通り 23
兵器 23
結婚 23
Jリーグ 23
全日本F3選手権 23
王国 23
サッカー 23
復讐 23
誕生 22
紋章 22
アリス 22

右の数字は出現回数です。

このうち一語の固有名詞として扱ったほうがいいもの(たとえば、**の冒険とか**の紋章とか)を人手で判断しました。
判断の補助としてmarisa-trieを使ってsuffixの検索を行うスクリプトを使いました。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import marisa_trie
import os
 
def is_valid_entry(entry):
    if '_(' in entry:
        return False
    return True
 
def build_trie(filename):
    reversed_entries = []
    for line in open(filename, 'r'):
        line = line.strip().decode('utf8')
        if is_valid_entry(line):
            reversed_entries.append(line[::-1])
    trie = marisa_trie.Trie(reversed_entries)
    trie.save('wikipedia_reverse.marisa')
 
if __name__ == '__main__':
    import optparse
 
    parser = optparse.OptionParser()
    parser.add_option("-f", "--filename", help="the path of input file")
    (opts, args) = parser.parse_args()
 
    if not os.path.exists('wikipedia_reverse.marisa'):
        build_trie(opts.filename)
    trie = marisa_trie.Trie()
    trie.load('wikipedia_reverse.marisa')
    while True:
        text = raw_input('>').decode('utf8')
        for i in trie.keys(text[::-1]):
            print i[::-1]


こうした作業によってできたリストがこちらです。見出しリストをMeCab用辞書に整形するときにこれを読み込んでlist型にして'文字列'.endswith(wikipedia_stop_suffixes)とかやって不要な見出しを弾くといいと思います。

一覧
の登場人物
の歴史
の国旗
の国歌
の国章
のスポーツ
の作品
の通貨
の鉄道
の音楽
の大統領
の世界遺産
の軍事
のオールナイトニッポン
の航空
の映画
の政治
の同位体
の野球
の首相
の政党
のメジャーリーグベースボール
のワールドシリーズ
の国会
のディスコグラフィ
の日本公開映画
のMLBオールスターゲーム
の関係
の経済
の交通
の年表
の連続ドラマ
のバレーボール
のアニメ
の奇跡
のF1世界選手権
のロードレース世界選手権
の日本シリーズ
のユニフォーム
のパラドックス
のNBA
の教育
の女性史
の料理
のユーロビジョン・ソング・コンテスト
の資格者配置路線
のアメリカ合衆国上院議員
の国鉄
のMLBドラフト
の空軍
のNFL
の登場キャラクター
のナショナルリーグチャンピオンシップシリーズ
の地理
の文化
のアメリカンリーグチャンピオンシップシリーズ
の文学
の世界ラリー選手権
のニュース
の海軍
の比較
の日本競馬
のオールナイトニッポンR
の町名
の陸軍
の観光地
の女王
の議会
の科学
の自転車競技
のチーム記録
の警察
の促進に関する法律
の問題
の政変
のNBAドラフト
のリスト
の韓国シリーズ
の宇宙飛行
の元素
の相撲
の年のラジオ
の事件
のJSL
のスーパーバイク世界選手権
の憲法
のユーロ硬貨
の電車
の戦争
の通り
の兵器
のJリーグ
の全日本F3選手権
のサッカー

今回のスクリプトや結果はgistにも置いてます。
https://gist.github.com/ikegami-yukino/8042385

ただ、これだけだとまだまだ不要見出し残ってるので、あとでまた何か書くかも。