WikipediaのHTMLダンプからワンライナーで情報抽出する

2021/10/19 にWikipediaのhtmlダンプの配布がアナウンスされた [0]
というわけで、rakulangを使ってダンプデータから情報抽出をする方法を紹介する。
今回は概要文の抽出とInfoboxの特定要素の抽出を行った。

# htmlダンプをダウンロードする

+ https://dumps.wikimedia.org/other/enterprise_html/ にアクセス

+ レジュームができるようにcオプションを指定してwgetでダウンロードする
  + jaはjapanで日本語版の意味 
  + NSというのはnamespaceの意味。ns=0は通常のWikipedia記事のあるところを指している

```
$ wget -c https://dumps.wikimedia.org/other/enterprise_html/runs/20211018/jawiki-NS0-20211018-ENTERPRISE-HTML.json.tar.gz
```

+ 解凍する

```
$ tar xvzf jawiki-NS0-20211018-ENTERPRISE-HTML.json.tar.gz
```

# 情報抽出

+ HTMLの仕様：https://www.mediawiki.org/wiki/Specs/HTML/2.2.0

## 概要文を取得する

+ `JSON::Tiny`と`DOM::Tiny`を使う
+ 手順
  + 1. `JSON::Tiny`で`article_body -> html` の位置の要素を取得する
     + `from-json($item)<article_body><html>`で`$item`番目の記事の`article_body -> html`のところを取得する
  + 2. `DOM::Tiny`で概要文の位置を抜き出す
     + sectionタグ内のpタグの中に概要文があるので`qq{section[data-mw-section-id="0"] p}` で位置を指定する。(qqについてはこちらを参照 [1])
     + Infoboxの有無により取得するべきpタグの要素を変更する。Infoboxがある場合はpタグのidにmwBwやmwAwが入ったものを選択する[2]。ない場合は先頭を取得する  
     + `map`で中の要素をイテレーションしつつ`$e.text`で文字列を取り出す。
     + 最後は文字列の配列を`.join`で結合する

コード:

```
$ head -n 5 jawiki_0.ndjson | raku -e 'use JSON::Tiny; use DOM::Tiny; for $*IN.lines -> $item { my $dom = DOM::Tiny.parse(from-json($item)<article_body><html>); my @nodes = $dom.find(qq{section[data-mw-section-id="0"] p}); gather if ($_=@nodes.grep(-> $n { $n.attr("id") // "" eq any("mwBw", "mwAw") })).elems > 0 { take .head } else { take @nodes.head }.head.map(-> $e { $e.text }).join.say }'
```

結果:

```
三原鉱（みはらこう、 Miharaite）は、1980年に発表された新鉱物で、東北大学の鉱床学者苣木浅彦などにより、岡山県の三原鉱山から発見された。 化学組成はCu4FePbBiS6で、斜方晶系。産出地の鉱山名から命名された。
ウォーターワールド (Waterworld)
三宅 春楼（みやけ しゅんろう、正徳2年11月15日（1712年12月13日） - 天明2年10月9日（1782年11月13日））は江戸時代中期の儒学者。懐徳堂第3代学主。名は正誼、字は子和、通称は才次郎。初代学主三宅石庵の子に当たる。
駅前温泉（えきまえおんせん）
岡田 優希（おかだ ゆうき、1996年5月13日 - ）は、神奈川県川崎市出身のサッカー選手。Jリーグ・FC町田ゼルビア所属。ポジションはフォワード。
```

## Infoboxから呪術廻戦の作者を抜き出す

+ ここでは大人気漫画『呪術廻戦』[3] を例題としてInfoboxからの情報抽出に取り組む。呪術廻戦の作者の名前を取得する。

```
$ grep --no-filename "同年35号より連載が再開" jawiki*json > jujutu.json
$ wc -l jujutu.json 
1 jujutu.json
```

+ `span -> mw-data`の箇所にInfoboxをJSON表現したものが格納されているらしい。今度は`XML::XPath`を使ってこれを取得していく

+ 手順
  + 1. `qq{//descendant::span[@id="mwBg"]/@data-mw}` でidがmwBgになっている箇所 [4]のspanを取得する。`@data-mw`で属性値が取れる
  + 2. `@data-mw`の属性値はJSON形式で格納されているため、`from-json($xpath.find...` でJSONを取り込む
  + 3. 取り込んだJSONは`parts -> template -> params`の階層になっている。grepを利用して作者の要素が格納されている位置を割り出して、その後でその部分を取り出してsayする

コード:

```
$ cat jujutu.json | raku -e 'use JSON::Tiny; use XML::XPath; for $*IN.lines -> $item { my $xpath = XML::XPath.new(xml => from-json($item)<article_body><html>); from-json($xpath.find(qq{//descendant::span[@id="mwBg"]/@data-mw}).head)<parts>.grep(*<template><params>{"作者"}).head<template><params>{"作者"}.say }'
```

結果:

```
{wt => [[芥見下々]]}
```

## おしまい

+ 新規に配布が開始されたWikipediaのHTMLダンプについて、rakulangを用いた情報抽出の方法を紹介した
+ 比較的重要な要素について仕様のページを探すことができなかった。本格的に使用するならもう少し調査が必要そう

## 脚注

[0] https://lists.wikimedia.org/hyperkitty/list/xmldatadumps-l@lists.wikimedia.org/thread/VV6JDNE5T4MBEIGYEPSAOUJKIMVPRSHH/
[1] https://docs.raku.org/language/quoting#index-entry-quote_qq-quote_%22_%22-Interpolation:_qq
[2] このページに少しだけIDの説明があるもののInfoboxとの関係は掲載されていなかった: https://www.mediawiki.org/wiki/Parsoid/MediaWiki_DOM_spec/Element_IDs
[3] https://ja.wikipedia.org/wiki/%E5%91%AA%E8%A1%93%E5%BB%BB%E6%88%A6
[4] もしかすると呪術廻戦以外の記事ではBw以外の文字列が入っているかも: https://www.mediawiki.org/wiki/Parsoid/MediaWiki_DOM_spec/Element_IDs