wikipediaからの情報抽出をステップバイステップでやっていくチュートリアルの資料です。
生物の学名と和名の対応が取りたかったのでwikiepdiaからゴリっと抜いてみる。ベースとなる学名は、学名DBを持ってきて、そいつとマッチングする形にしないと表記ゆれの問題が大きい。
一応多言語で同じアプローチは取れるはず。
でかいデータから情報を抜いていくには、もとのデータを残しながら、フィルタをして取り出していくパイプラインを構築しておくと手戻りや、他の情報も一緒に取り出すなどがやりやすいので、この手順を覚えておくと良いです。
学名DB
学名も表記ゆれやsynonymも多いのでそこに関してはリファレンスを用いたい。学説が変われば同じようにラベルの張替えだけすれば良いので、まずはべーすとなるものが必要
植物はこれを使う
-
https://github.com/rdmpage/the-plant-list-1-1
- The Plant Listをクローリングしたものっぽい。自分でスペース区切りでjoinしろ的な感じ
動物はなんかあるんかいね。虫とかはありそう。
生物分類表
生物の系統についてまとめられているwikipediaのテンプレート。系統の階層の情報や、和名と学名の情報が含まれている。かなり複雑でかつCGMなので適当に記載されているので結構機械で扱うには面倒っぽい。
テンプレートなので、どの記事にこの表が記載されているかはテンプレートへのリンク情報を見れば良い
テンプレートへのリンク
Wiki template inclusion link records.
となっているwikipediaのダンプデータを利用する。11月19日現在ではこれが最新
こいつはmysqlのダンプのデータ。mysqlでいろいろやるの面倒なので sqlite3のsqlに変換して、sqliteで処理する。テキストをそのままパースしても良いんだけど、dbのサイズも1Gぐらいで大したこと無いのでコマンドラインだけで楽をします。
mysqlのdumpをsqlite3形式にするにはmysql2sqliteを使うと楽
適当に作業
wget https://raw.githubusercontent.com/dumblob/mysql2sqlite/master/mysql2sqlite
chmod +x mysql2sqlite
wget https://dumps.wikimedia.org/jawiki/20181101/jawiki-20181101-templatelinks.sql.gz
gunzip jawiki-20181101-templatelinks.sql.gz
./mysql2sqlite jawiki-20181101-templatelinks.sql |sqlite3 templatelinks.db
sqlite3 templatelinks.db 'SELECT tl_from FROM templatelinks WHERE tl_title="生物分類表"' > ja_taxsobox.txt
これで、生物分類表を含む記事について1行1記事idになったテキストファイルja_taxsobox.txt
が作成される
本文データを処理する
wikipediaの本文データはくっそでかいので、処理時間はかなり掛かるはず。メモリで死ぬので、streaming apiで読むべき。解答しておくのもなんか嫌なので、オンラインで解凍しながらパースしていきたい。
wget https://dumps.wikimedia.org/jawiki/20181101/jawiki-20181101-pages-articles.xml.bz2
生物分類表テンプレートを含む記事のみ取り出し
雑なスクリプトを書く。JSON Linesで出力したかったので、xmltodictというライブラリを入れた
import bz2
import json
import sys
from xml.etree import ElementTree
import xmltodict
WIKIPEDIA_NAMESPACE = 'http://www.mediawiki.org/xml/export-0.10/'
def translate_path(path):
return '/'.join('{%s}%s' % (WIKIPEDIA_NAMESPACE, tag) for tag in path.split('/'))
if __name__ == "__main__":
ids = []
for line in sys.stdin:
line = line.strip()
ids.append(line)
ids = set(ids)
page_tag = translate_path('page')
text_tag = translate_path('revision/text')
title_tag = translate_path('title')
page_id_tag = translate_path('id')
ElementTree.register_namespace('', WIKIPEDIA_NAMESPACE)
with bz2.open(sys.argv[1], mode="rt") as bz2f:
context = ElementTree.iterparse(bz2f, events=('start', 'end'))
event, root = next(context)
for event, element in context:
if event == 'end' and element.tag == page_tag:
if element.findtext(page_id_tag) in ids:
print(json.dumps(
xmltodict.parse(ElementTree.tostring(element, encoding="unicode")),
ensure_ascii=False))
root.clear()
これで必要な記事だけが入ったjsonl形式のファイルが作成できる
cat ja_taxsobox.txt | python filter_articles.py jawiki-20181101-pages-articles.xml.bz2 > ja_taxsobox.jsonl
テンプレートのマイニング
本文データからのtemplate部分の切り出しはmwparserfromhellを使うと楽が出来ると思う。
JSON Linesで作っているので、一行づつ読み込んで、mwparserfromhellでtemplateだけを取り出す。
import json
import sys
import mwparserfromhell
if __name__ == "__main__":
for line in sys.stdin:
line = line.strip()
j = json.loads(line)
id = j["page"]["id"]
title = j["page"]["title"]
text = j["page"]["revision"]["text"]["#text"]
parsed = mwparserfromhell.parse(text)
templates = parsed.filter_templates()
for template in templates:
if template.name.matches("生物分類表"):
print(json.dumps({"id": id, "title": title, "template": str(template)}, ensure_ascii=False))
cat ja_taxsobox.jsonl| python extract_template.py > ja_taxsobox_templates.jsonl
templateから学名と和名のペア候補を作成
なんか考える
リファレンスの学名データからのマッチング
simstringを使って類似文字列のマッチングを行う。リファレンスデータから類似文字列DBを構築してスコアリング出来るようにしておく