Wikipediaダンプファイルの前処理とMeCabによる大容量データの分かち書き

最近Wikipediaのダンプファイルを読み込んで作業することがあったので，その際に調べたことや読み込む際の注意点などをまとめようと思います．

# 記事の対象者
- 自然言語処理データセットとしてWikipediaのダンプファイルを使いたい人
- MeCabによる分かち書きを大容量データに対して行いたい人
- 大容量データを読み込んで処理をする際にOOMで困っている人

# Wikipediaダンプファイルのダウンロード
[このリンク](https://dumps.wikimedia.org/jawiki/20200601/jawiki-20200601-pages-articles-multistream.xml.bz2)からWikipediaダンプファイルをダウンロードするか下記のwgetを実行．

```
> wget https://dumps.wikimedia.org/jawiki/20200601/jawiki-20200601-pages-articles-multistream.xml.bz2
```

# WikiExtractorを使って展開
pipでインストール．

```
> pip install wikiextractor
```

展開を実行．今回は出力先をテキストファイル（wiki.txt）一つにまとめて保存します．-oにパスを指定することで任意のディレクトリにも保存可能です．

```
> python -m wikiextractor.WikiExtractor jawiki-latest-pages-articles.xml.bz2 -o - --processes 4 > wiki.txt
```

# wiki.txtの前処理
中身を確認してみるとわかるが，docタグが残っているので念のため取り除いておく．

```
> head wiki.txt
------------------------------------------------------
<doc id="17230" url="https://ja.wikipedia.org/wiki?curid=17230" title="今川義元">
今川義元

今川 義元（いまがわ よしもと）は、戦国時代の駿河国及び遠江国の守護大名・戦国大名。今川氏第11代当主。姉妹との婚姻関係により、武田信玄や北条氏康とは義理の兄弟にあたる。「海道一の弓取り」の異名を持つ東海道の広大な地域の支配者。

寄親・寄子制度を設けての合理的な軍事改革等の領国経営のみならず、外征面でも才覚を発揮して今川氏の戦国大名への転身を成功させた。所領も駿河・遠江から、三河や尾張の一部にまで領土を拡大させた。戦国時代における今川家の最盛期を築き上げるも、尾張国に侵攻した際に行われた桶狭間の戦いで織田信長軍に敗れて毛利良勝（新助）に討ち取られた。

永正16年（1519年）、今川氏親の三男として生まれる。母は父の正室である中御門宣胤の娘（寿桂尼）。ただし、義元は本来は側室の子で花倉の乱後に寿桂尼と養子縁組をしたとする説もある（後述）。生まれた時は既に跡継ぎとして、同母兄の氏輝、及び彦五郎がいたために4歳で仏門に出され、駿河国富士郡瀬古善得寺の琴渓承舜に預けられた。享禄2年（1529年）に承舜が没したために、彼の弟子であった九英承菊（後の太原雪斎）がその役割を継承した。その後、雪斎と共に建仁寺に入り常庵龍崇の元で得度し栴岳承芳（せんがくしょうほう）となった。さらに雪斎と共に妙心寺で大休宗休に学び学識を深めた。

その後、氏輝の命を受けて京都から駿河に戻るが、その直後の天文5年（1536年）に氏輝が急死する。この時点ではまだ兄の彦五郎がいたために継承権はなかったが、彦五郎までもが氏輝と同日に死亡したために継承権が巡ってきた。氏輝・彦五郎と同じ寿桂尼所生であることも後押しとなり、重臣たちから還俗を乞われた承芳は主君であり本流に当たる征夷大将軍・足利義晴から偏諱を賜り、義元と名乗った。だが当主継承は有力家臣の福島（ふくしま）氏の反対で混迷化し、最終的に福島氏は自家の血を引く義元の異母兄・玄広恵探を当主として掲げて反旗を翻した（花倉の乱）。
```

[こちら](https://applingo.tokyo/article/1252)の記事を参考にタグを削除する．

```
> cat wiki.txt | sed '/^<[^>]*>$/d' > wiki_removed_doc_tag.txt
```

# MeCabによる分かち書き
日本語は空白区切りになっておらず，単語を上手く認識する必要がでてきます．Sentencepieceなどはある程度空白がない言語にも対応しているようですが，事前にMeCabで分かち書きをしておくことでいい感じになった例もあるみたいです．

MeCabのインストール

```
# windows以外
> pip install mecab-python3
# windows
> pip install mecab-python-windows
```

MeCabを使った分かち書き

```python
import MeCab

text = "分かち書きは簡単ではありませんが，チャレンジングなタスクです．"

tokenizer = MeCab.Tagger("-Owakati") # 分かち書きモード
tokens = tokenizer.parse(text).split()
print(tokens)
# => ['分かち書き', 'は', '簡単', 'で', 'は', 'あり', 'ませ', 'ん', 'が', '，', 'チャレンジング', 'な', 'タスク', 'です', '．']
```

このtextに無理やりwiki_removed_doc_tag.txtを入れてもいいのですが，今回はメモリへの負担を考えて一行ずつ読み込んでいくことにします．ちなみにWindowsだとopen()のオプションにエンコーディングを指定しないとエラーが出るのでencoding="utf-8_sig"を指定してください．

```python:mecab_tokenization.py
import MeCab

file_path = "wiki_removed_doc_tag.txt"
output_path = "wiki_mecab_space_separated.txt"
tokenizer = MeCab.Tagger("-Owakati")
output_text = "" # ここに一行ずつ結果を追加していく

# 読み込んで処理
with open(file_path, "r") as f_in:
  for line in f_in:
    tokens = tokenizer.parse(line).split() # 分かち書き
    text = " ".join(tokens) # 空白区切りにする
    output_text += text

# 保存する
with open(output_path, "w") as f_out:
  f_out.write(output_text)

```

こうすることで，OOMを発生させずにメモリに全載せすることなく加工できます．

ここまで来たら空白区切りの文が大量に手に入るのでsentencepieceの学習に使うなどして活用できます．