できるだけ楽してwikipedia等を使って生物の学名と一般名の対応を取る

wikipediaからの情報抽出をステップバイステップでやっていくチュートリアルの資料です。

生物の学名と和名の対応が取りたかったのでwikiepdiaからゴリっと抜いてみる。ベースとなる学名は、学名DBを持ってきて、そいつとマッチングする形にしないと表記ゆれの問題が大きい。

一応多言語で同じアプローチは取れるはず。

でかいデータから情報を抜いていくには、もとのデータを残しながら、フィルタをして取り出していくパイプラインを構築しておくと手戻りや、他の情報も一緒に取り出すなどがやりやすいので、この手順を覚えておくと良いです。

# 学名DB

学名も表記ゆれやsynonymも多いのでそこに関してはリファレンスを用いたい。学説が変われば同じようにラベルの張替えだけすれば良いので、まずはべーすとなるものが必要

植物はこれを使う

- https://github.com/rdmpage/the-plant-list-1-1
  - The Plant Listをクローリングしたものっぽい。自分でスペース区切りでjoinしろ的な感じ

動物はなんかあるんかいね。虫とかはありそう。

# 生物分類表

- https://ja.wikipedia.org/wiki/Template:%E7%94%9F%E7%89%A9%E5%88%86%E9%A1%9E%E8%A1%A8

生物の系統についてまとめられているwikipediaのテンプレート。系統の階層の情報や、和名と学名の情報が含まれている。かなり複雑でかつCGMなので適当に記載されているので結構機械で扱うには面倒っぽい。

テンプレートなので、どの記事にこの表が記載されているかはテンプレートへのリンク情報を見れば良い

## テンプレートへのリンク

`Wiki template inclusion link records.`となっているwikipediaのダンプデータを利用する。11月19日現在ではこれが最新

- https://dumps.wikimedia.org/jawiki/20181101/jawiki-20181101-templatelinks.sql.gz

こいつはmysqlのダンプのデータ。mysqlでいろいろやるの面倒なので　sqlite3のsqlに変換して、sqliteで処理する。テキストをそのままパースしても良いんだけど、dbのサイズも1Gぐらいで大したこと無いのでコマンドラインだけで楽をします。

mysqlのdumpをsqlite3形式にするにはmysql2sqliteを使うと楽

- https://github.com/dumblob/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というライブラリを入れた

```filter_article.py
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を使うと楽が出来ると思う。

- https://mwparserfromhell.readthedocs.io/en/latest/

JSON Linesで作っているので、一行づつ読み込んで、mwparserfromhellでtemplateだけを取り出す。

```extract_template.py
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を構築してスコアリング出来るようにしておく