Wikipediaの大量データをMySQLに保存する

仕事でビッグデータとか機械学習周りの分野に手を出していきたい今日この頃なのですが、練習に業務用データを使うわけにもいきません。幸いWikipediaが全コンテンツを配布してくれているのでこのデータを取り込んでみました。

## Wikipedia配布データの取得

Wikipediaのデータ配布については、サイト内の [Wikipedia:データベースダウンロード](https://ja.wikipedia.org/wiki/Wikipedia:%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89) ページに情報があります。

ここから配布先の [ページ](https://dumps.wikimedia.org/jawiki/)に飛ぶと、各用途に応じてテキストファイル、XML、MySQLのダンプが配布されています。

今回はページ情報とカテゴリ・ページ間のリンク情報、ページ間のリンク情報を取得してみました。

ページ情報
[jawiki-latest-page.sql.gz](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-page.sql.gz)

カテゴリ間のリンク情報
[jawiki-latest-categorylinks.sql.gz](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-categorylinks.sql.gz)

ページ間のリンク情報
[jawiki-latest-pagelinks.sql.gz](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pagelinks.sql.gz)



## 解凍 

ダウンロードしたら解凍しておきます。
jawiki-latest-categorylinks.sql
jawiki-latest-pagelinks.sql
jawiki-latest-page.sql


## MySQLへインポート

お手元のMySQLにログインして適当なdatabaseを準備します。

```console:
mysql> create database jawikipedia;
```

後は順次インポートしていくだけです。

```
$ mysql -u root jawikipedia < jawiki-latest-page.sql
$ mysql -u root jawikipedia < jawiki-latest-pagelinks.sql
$ mysql -u root jawikipedia < jawiki-latest-categorylinks.sql
```

データが大きくて実行時間は結構かかります。手元のMacでかかった大体の時間を参考までに載せておきます。
jawiki-latest-page.sql・・・数分で終わった。
jawiki-latest-pagelinks.sql・・・10分くらい。
jawiki-latest-pagelinks.sqlは・・・8時間くらい。途中PCがスリープになっていたので本当はもっと早かったかもしれません。



## 取り込んだデータ


### テーブル構造

#### page
```console
mysql> describe page;
+--------------------+---------------------+------+-----+---------+----------------+
| Field              | Type                | Null | Key | Default | Extra          |
+--------------------+---------------------+------+-----+---------+----------------+
| page_id            | int(8) unsigned     | NO   | PRI | NULL    | auto_increment |
| page_namespace     | int(11)             | NO   | MUL | 0       |                |
| page_title         | varbinary(255)      | NO   |     |         |                |
| page_restrictions  | varbinary(255)      | NO   |     |         |                |
| page_counter       | bigint(20) unsigned | NO   |     | 0       |                |
| page_is_redirect   | tinyint(1) unsigned | NO   | MUL | 0       |                |
| page_is_new        | tinyint(1) unsigned | NO   |     | 0       |                |
| page_random        | double unsigned     | NO   | MUL | 0       |                |
| page_touched       | varbinary(14)       | NO   |     |         |                |
| page_links_updated | varbinary(14)       | YES  |     | NULL    |                |
| page_latest        | int(8) unsigned     | NO   |     | 0       |                |
| page_len           | int(8) unsigned     | NO   | MUL | 0       |                |
| page_content_model | varbinary(32)       | YES  |     | NULL    |                |
+--------------------+---------------------+------+-----+---------+----------------+
```

Pageテーブルにはコンテンツは含まれていません。別途jawiki-latest-pages-articles.xml.bz2から取得する必要があります。　


#### categorylinks
```
mysql> describe categorylinks;
+-------------------+------------------------------+------+-----+-------------------+-----------------------------+
| Field             | Type                         | Null | Key | Default           | Extra                       |
+-------------------+------------------------------+------+-----+-------------------+-----------------------------+
| cl_from           | int(8) unsigned              | NO   | PRI | 0                 |                             |
| cl_to             | varbinary(255)               | NO   | PRI |                   |                             |
| cl_sortkey        | varbinary(230)               | NO   |     |                   |                             |
| cl_timestamp      | timestamp                    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| cl_sortkey_prefix | varbinary(255)               | NO   |     |                   |                             |
| cl_collation      | varbinary(32)                | NO   | MUL |                   |                             |
| cl_type           | enum('page','subcat','file') | NO   |     | page              |                             |
+-------------------+------------------------------+------+-----+-------------------+-----------------------------+
```

cl_fromがpage.page_idに対応します。

#### pagelinks

```
mysql> describe pagelinks;
+-------------------+-----------------+------+-----+---------+-------+
| Field             | Type            | Null | Key | Default | Extra |
+-------------------+-----------------+------+-----+---------+-------+
| pl_from           | int(8) unsigned | NO   | PRI | 0       |       |
| pl_namespace      | int(11)         | NO   | PRI | 0       |       |
| pl_title          | varbinary(255)  | NO   | PRI |         |       |
| pl_from_namespace | int(11)         | NO   | MUL | 0       |       |
+-------------------+-----------------+------+-----+---------+-------+
```
 pl_fromがpage.page_idに対応します。


### レコード数

取り込まれたレコード数です。pagelinksは取り込み途中の数値です。

```
mysql> select count(*) from page;
+----------+
| count(*) |
+----------+
|  2877211 |
+----------+
1 row in set (2.30 sec)

mysql> select count(*) from categorylinks;
+----------+
| count(*) |
+----------+
|  6382442 |
+----------+
1 row in set (6.97 sec)

mysql> select count(*) from pagelinks;
+-----------+
| count(*)  |
+-----------+
| 104787490 |
+-----------+
1 row in set (1 min 52.99 sec)
```


### ページのカテゴリの一覧を取得する

```
mysql> select cl_to
    -> from categorylinks
    -> where cl_from in (
    ->   select page_id 
    ->   from page
    ->   where page_namespace=0
    ->     and page_title='機械学習'
    -> );
+-----------------------------------------+
| cl_to                                   |
+-----------------------------------------+
| サイバネティックス                      |
| 人工知能                                |
| 学習                                    |
| 機械学習                                |
| 解消済み仮リンクを含む記事              |
+-----------------------------------------+
5 rows in set (0.01 sec)
```

### ページ内のリンクを取得する

```
mysql> select pl_namespace,pl_title
    -> from pagelinks
    -> where pl_from in (
    ->   select page_id 
    ->   from page
    ->   where page_namespace=0
    ->     and page_title='機械学習'
    -> );
+--------------+-----------------------------------------------------------+
| pl_namespace | pl_title                                                  |
+--------------+-----------------------------------------------------------+
|            0 | Apache_Mahout                                             |
|            0 | F検定                                                     |
|            0 | G検定                                                     |
|            0 | ID3                                                       |
|            0 | ISBN                                                      |
中略
+--------------+-----------------------------------------------------------+
244 rows in set (0.00 sec)

```

## ページコンテンツをMySQLへ取り込む

**注意：これ以降は最終的に失敗しています。**

ページのコンテンツは、jawiki-latest-pages-articles.xml.bz2 にあるのですが、巨大なXMLファイルで扱いが難しいです。GroovyのXmlSlurperでパースしてみましたが、あえなくOutOfMemoryError.になりました。（SAXなのになぜ？）

コンテンツ
[jawiki-latest-pages-articles.xml.bz2](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2)

コンテンツを分割したもの？
[jawiki-latest-pages-articles1.xml.bz2](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles1.xml.bz2)
[jawiki-latest-pages-articles2.xml.bz2](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles2.xml.bz2)
[jawiki-latest-pages-articles3.xml.bz2](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles3.xml.bz2)
[jawiki-latest-pages-articles4.xml.bz2](https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles4.xml.bz2)


「Wikipedia:データベースダウンロード」ページにはxml2sqlというツールへの[リンク](https://meta.wikimedia.org/wiki/Data_dumps/xml2sql)があるのでそれを試してみます。

### xml2sqlのダウンロード
上記リンク先から、さらに[GitHub](https://github.com/Tietew/mediawiki-xml2sql)のぺージへ飛びます。

https://github.com/Tietew/mediawiki-xml2sql からプロジェクトをgit cloneします。

### make
クローンしたプロジェクト内のINSTALLファイルに手順があります。

```
./configure
make
sudo make install
```

INSTALLファイルには、UNIX、Linux、Windowsに対しての説明が書いてありますが、Macでもビルドできました。

成功するとxml2sqlコマンドが作成されます。
/usr/local/bin/xml2sql

### 実行してみる
```
cat jawiki-latest-pages-articles.xml |  xml2sql -m 
unexpected element <dbname>
xml2sql: parsing aborted at line 4 pos 12.
```

dbnameエレメントが認識できずにエラーになります。もともとxml2sqlは、英語版用でja.wikipeditaは対象外なのかもしれません。orz...

試しにエラーになるタグを片っ端からスキップしてみます。

```
cat jawiki-latest-pages-articles.xml | grep -v "<dbname>"
 | grep -v "<ns>" | grep -v "<parentid>" | grep -v "<model>"
 | grep -v "<format>" | grep -v "<sha1>" | grep -c "<redirect>" | xml2sql -m

(実際は1行です。拙いgrepですみません。)
```

実行結果

```
   587  1 25 00:07 page.sql
     0  1 24 23:23 page.txt
   573  1 25 00:07 revision.sql
     0  1 24 23:23 revision.txt
113207  1 25 00:07 text.sql
     0  1 24 23:23 text.txt
```
何やらファイルが作られますが、しばらくたってもファイルサイズが大きくならず、応答もないままになりました。上手く動作していないようです。

今回はここで諦めます。