Wikipedia のサマリをターミナルから取得

# はじめに
人間誰しも、ターミナルで作業中にウィキペディアで何か調べたいがブラウザに移動するのは面倒くさい、みたいな状況がよくあると思います（?）
ということで、ある単語の Wikipedia 記事を取得するシェルスクリプト (Bash) を書いてみました。
Wikipedia の記事は冒頭文がサマリになっていて、概要を知るにはそこだけ見れば十分みたいなことも多いため、その冒頭部のみ抽出することとします。

# 作成したもの
https://github.com/chgzm/wikishell

## 使用例
`./wikishell <検索ワード>` で冒頭文を表示します


```Terminal
$ ./wikishell Linux
Linux is a family of open source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991 by Linus Torvalds. Linux is typically packaged in a Linux distribution (or distro for short).
```

検索語にスペースが入る場合は `"Gentoo Linux"` のように二重引用符で囲みます。 

```Terminal
$ ./wikishell "Gentoo Linux"
Gentoo Linux  is a Linux distribution built using the Portage package management system. Unlike a binary software distribution, the source code is compiled locally according to the user's preferences and is often optimized for the specific type of computer. Precompiled binaries are available for some larger packages or those with no available source code.
```

日本人なので `-j` or `--japanese` で日本語版の検索もできるようにしました。

```Terminal
$ ./wikishell -j Linux
Linux（リナックス、他の読みは後述）とは、Unix系オペレーティングシステムカーネルであるLinuxカーネル、およびそれをカーネルとして周辺を整備したシステムである（GNU/Linuxシステムも参照）。

$  ./wikishell --japanese Qiita
Qiita（キータ）は、Incrementsが運営するプログラミング情報のナレッジコミュニティ。2016年現在で日本最大のプログラマーコミュニティとされている。
```

## スクリプト全体
```bash:wikishell
#!/bin/bash

function usage() {
    echo -e "Usage: "
    echo -e "  $0 [Options] <word>"
    echo -e ""
    echo -e "Options:"
    echo -e "  -j, --japanese   use japanese api"
    echo -e "  -h, --help       display help"
}

url="https://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvslots=main&format=xml&titles="
ja_flag=0

for OPT in "$@"
do
    case $OPT in
        '-j' | '--japanese')
            url="https://ja.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvslots=main&format=xml&titles="
            ja_flag=1
            ;;
        '-h' | '--help')
            usage
            exit 0
            ;;
        -*)
            echo -e "Invalid option '${OPT}'" 1>&2
            usage
            exit 1
            ;;
        *)
            word="${1// /_}"
            ;;
    esac
    shift
done

if [[ -z "$word" ]]; then
    usage
    exit 1
fi

content="$(\
    curl -sL "${url}${word}" \
  | xmllint --xpath "/api/query/pages/page/revisions/rev/slots/slot" - \
  | xmllint --encode utf-8 -\
)"

# Redirect
if (($ja_flag == 0)); then
    redirect="REDIRECT"
else
    redirect="転送"
fi

if [[ "$(echo "${content}" | head -n 2 | grep ${redirect})" ]]; then
    redirect_word="$(echo ${content} | sed -e "s/.*#${redirect} \[\[\(.*\)\]\].*/\1/g")"
    echo -e "REDIRECT to \"${redirect_word}\""
    content="$(\
        curl -sL "${url}${redirect_word// /_}" \
      | xmllint --xpath "/api/query/pages/page/revisions/rev/slots/slot" - \
      | xmllint --encode utf-8 -\
   )"
fi

echo "${content}" \
  | sed -e "s/&lt;/</g; s/&gt;/>/g" \
  | grep "^'''" \
  | head -n 1 \
  | sed -e "s/'\{2,4\}\([^']*\)'\{2,4\}/\1/g" \
  | sed -e "s/\[\[\([^]]*\)|\([^]]*\)\]\]/\2/g" \
  | sed -e "s/\[\[\([^]]*\)#\([^]]*\)|\([^]]*\)\]\]/\3/g" \
  | sed -e "s/\[\[\([^]]*\)\]\]/\1/g" \
  | sed -e "s/{{[^|]*|\([^}]*\)}}/\1/g" \
  | sed -e "s/ ({{.*}})//g" \
  | perl -pe "s/<ref.*?<\/ref>//g" \
  | perl -pe "s/<ref.*?\/>//g" \
  | perl -pe "s/<!--.*?-->//g" \
  | perl -pe "s/ \([audio|pronounced|or].*?\)//g"
```

# 解説
## エンドポイント
[API:メイン ページ](https://www.mediawiki.org/wiki/API:Main_page/ja) によると、英語版/日本語版の API エンドポイントは以下。
- https://en.wikipedia.org/w/api.php
- https://ja.wikipedia.org/w/api.php

## XML の取得
https://www.mediawiki.org/wiki/API:Query/ja や [MediaWiki APIを使ってWikipediaの情報を取得](https://qiita.com/yubessy/items/16d2a074be84ee67c01f) を参考に、以下のパラメータで記事の XML を取得することとします。titles= の後に、検索するワードを指定します。

`https://ja.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvslots=main&format=xml&titles=<word>`

```Terminal
$ curl -s "https://ja.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvslots=main&format=xml&titles=Linux"
<?xml version="1.0"?><api batchcomplete=""><query><pages><page _idx="436742" pageid="436742" ns="0" title="Linux"><revisions><rev><slots><slot contentmodel="wikitext" contentformat="text/x-wiki" role="main" xml:space="preserve">{{Infobox OS
| name                   = Linux
| logo                   = [[ファイル:NewTux.svg|150px|Linuxのマスコット「Tux」]]
| screenshot             =
| caption                = Linuxのマスコット「Tux」
| family                 = [[Unix系]]
| source_model           = [[FLOSS]]
| released               = {{release date and age|1991}}
| frequently_updated     = yes
| marketing_target       = [[サーバ]]、[[組み込みシステム]]、[[パソコン]]、[[メインフレーム]]、[[スーパーコンピューター]]など
| language               = 多言語対応
| kernel_type            = [[モノリシックカーネル]]
| userland               = 様々
| ui                     = 多種
| license                = [[Linuxカーネル]]は[[GNU General Public License|GNU GPL]]
| working_state          = 開発進行中
| supported_platforms    = [[Linuxカーネル#動作しているアーキテクチャ]]を参照
| updatemodel            =
| package_manager        = 多種
}}
'''Linux'''（リナックス、他の読みは[[#「Linux」の読み方|後述]]）とは、[[Unix系]][[オペレーティングシステム]][[カーネル]]である[[Linuxカーネル]]、およびそれをカーネルとして周辺を整備したシステムである（[[GNU/Linuxシステム]]も参照）。
&lt;!--
OSとしての利用状況は、デスクトップOSという用途では市場シェアが1%台と低迷しWindowsに惨敗しているが&lt;ref&gt;https://news.mynavi.jp/article/20180507-627002/&lt;/ref&gt;、サーバや組み込みOSとしては一定のシェアを有し比較的善戦している。
--&gt;
==概要==
Linuxは、狭義には[[Linuxカーネル]]、広義にはそれを[[カーネル]]として用いたオペレーティングシステムを指す。Linuxは[[Unix系]]（{{lang-en-short|Unix like}}、Unixライク）[[オペレーティングシステム]] (OS) の1つとされる。カタカナでは「リナックス」と表記されることが多い（[[#「Linux」の読み方|「Linux」の読み方]]を参照）。Linuxは、[[スーパーコンピュータ]]・[[メインフレーム]]・[[サーバ]]・[[パーソナルコンピュータ]]・[[組み込みシステム]]（携帯電話やテレビなど）など、幅広い種類の[[ハードウェア]]で使用されている。
...
--- 以下略 ---
```

## XML のパース
本文は `/api/query/pages/page/revisions/rev/slots/slot` ノードにあるため、`xmllint` と `--xpath` を使って本文を抽出します。`xmllint` を一度作用させるだけだと日本語が文字参照形式になってしまうため、再度 `xmllint` にエンコード指定してデータを流します（もっとうまいやり方があるかもしれません）

```bash
content="$(\
    curl -sL "${url}${word}" \
  | xmllint --xpath "/api/query/pages/page/revisions/rev/slots/slot" - \
  | xmllint --encode utf-8 -\
)"
```

## マークアップ除去等 
- '''Linux''' のように、本文の最初は強調のマークアップで始まっていると信じ、その行のみを抜き出します
- [Help:早見表](https://ja.wikipedia.org/wiki/Help:%E6%97%A9%E8%A6%8B%E8%A1%A8) や実際の記事を見ながら、ひたすらマークアップを置換します。（なんとなく全部 `sed` でやりたかったのですが、`sed` でうまいこと最短一致ができなかったため、一部 `perl` を使用してます）

```bash
echo "${content}" \
  | sed -e "s/&lt;/</g; s/&gt;/>/g" \
  | grep "^'''" \
  | head -n 1 \
  | sed -e "s/'\{2,4\}\([^']*\)'\{2,4\}/\1/g" \
  | sed -e "s/\[\[\([^]]*\)|\([^]]*\)\]\]/\2/g" \
  | sed -e "s/\[\[\([^]]*\)#\([^]]*\)|\([^]]*\)\]\]/\3/g" \
  | sed -e "s/\[\[\([^]]*\)\]\]/\1/g" \
  | sed -e "s/{{[^|]*|\([^}]*\)}}/\1/g" \
  | sed -e "s/ ({{.*}})//g" \
  | perl -pe "s/<ref.*?<\/ref>//g" \
  | perl -pe "s/<ref.*?\/>//g" \
  | perl -pe "s/<!--.*?-->//g" \
  | perl -pe "s/ \([audio|pronounced|or].*?\)//g"
```

# まとめ
- ターミナルから離れずに Wikipedia のサマリが読めます。
  - ただ、うまく表示できない記事も（それはもうめっちゃ）あるので、結局ブラウザ開くことになるかもしれません。
- なんにせよ正規表現の勉強にはなりました。