言語処理100本ノック-28:MediaWikiマークアップの除去

[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)[「第3章: 正規表現」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch3)の[28本目「MediaWikiマークアップの除去」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec28)記録です。今回でマークアップ除去系は終わりです。新しく覚える内容はなく、今まで習ったことを実践で使うノックです。

# 参考リンク

|リンク|備考|
|:--|:--|
|[028.MediaWikiマークアップの除去.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/03.%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE/028.MediaWiki%E3%83%9E%E3%83%BC%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97%E3%81%AE%E9%99%A4%E5%8E%BB.ipynb)|回答プログラムのGitHubリンク|
|[素人の言語処理100本ノック:28](https://qiita.com/segavvy/items/8c4567ec1124320d3354)|多くのソース部分のコピペ元|
|[ゼロから覚えるPython正規表現の基本とTips](https://qiita.com/FukuharaYohei/items/459f27f0d7bbba551af7)|当ノックで学習した内容を整理しました|
|[正規表現 HOWTO](https://docs.python.org/ja/3/howto/regex.html#regex-howto)|Python公式の正規表現How To|
|[re --- 正規表現操作](https://docs.python.org/ja/3/library/re.html)|Python公式のreパッケージ説明|
|[Help:早見表](https://ja.wikipedia.org/wiki/Help:%E6%97%A9%E8%A6%8B%E8%A1%A8)|Wikipediaの代表的なマークアップの早見表|

# 環境

|種類|バージョン|内容|
|:--|:--|:--|
|OS|Ubuntu18.04.01 LTS|仮想で動かしています|
|pyenv|1.2.15|複数Python環境を使うことがあるのでpyenv使っています|
|Python|3.6.9|pyenv上でpython3.6.9を使っています<br>3.7や3.8系を使っていないことに深い理由はありません<br>パッケージはvenvを使って管理しています|

上記環境で、以下のPython追加パッケージを使っています。通常のpipでインストールするだけです。

|種類|バージョン|
|:--|:--|
|pandas|0.25.3|

# [第3章: 正規表現](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch3)
## 学習内容
> Wikipediaのページのマークアップ記述に正規表現を適用することで，様々な情報・知識を取り出します．
>> 正規表現, JSON, Wikipedia, InfoBox, ウェブサービス

## ノック内容
> Wikipediaの記事を以下のフォーマットで書き出したファイル[jawiki-country.json.gz](http://www.cl.ecei.tohoku.ac.jp/nlp100/data/jawiki-country.json.gz)がある．

> - 1行に1記事の情報がJSON形式で格納される
> - 各行には記事名が"title"キーに，記事本文が"text"キーの辞書オブジェクトに格納され，そのオブジェクトがJSON形式で書き出される
> - ファイル全体はgzipで圧縮される

> 以下の処理を行うプログラムを作成せよ．

### [28. MediaWikiマークアップの除去](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec28)
> 27の処理に加えて，テンプレートの値からMediaWikiマークアップを可能な限り除去し，国の基本情報を整形せよ．

### 課題補足(「MediaWikiマークアップ」について)
「MediaWikiマークアップを可能な限り」ですか・・・
ファイル内の以下の部分を正規表現で抽出しています。

|種類|書式|参照元|
|:-:|:-:|:-:|
|ファイル|[[ファイル:Wikipedia-logo-v2-ja.png&#124;thumb&#124;説明文]]|[Help:早見表](https://ja.wikipedia.org/wiki/Help:%E6%97%A9%E8%A6%8B%E8%A1%A8)|
|外部リンク|[http://www.example.org]<br>[http://www.example.org 表示文字]|[Help:早見表](https://ja.wikipedia.org/wiki/Help:%E6%97%A9%E8%A6%8B%E8%A1%A8)|
|Template:Lang|{{lang&#124;言語タグ&#124;文字列}}|[Template:Lang](https://ja.wikipedia.org/wiki/Template:Lang)|
|HTMLタグ|`<tag>`|なし|

# 回答
## 回答プログラム [028.MediaWikiマークアップの除去.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/03.%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE/028.MediaWiki%E3%83%9E%E3%83%BC%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97%E3%81%AE%E9%99%A4%E5%8E%BB.ipynb)

```python
from collections import OrderedDict
from pprint import pprint
import re

import pandas as pd

def extract_by_title(title):
    df_wiki = pd.read_json('jawiki-country.json', lines=True)
    return df_wiki[(df_wiki['title'] == title)]['text'].values[0]

wiki_body = extract_by_title('イギリス')

basic = re.search(r'''
                    ^\{\{基礎情報.*?\n  #検索語句(\はエスケープ処理)、非キャプチャ、非貪欲
                    (.*?)              #任意の文字列
                    \}\}               #検索語句(\はエスケープ処理)
                    $                  #文字列の末尾
                    ''', wiki_body, re.MULTILINE+re.VERBOSE+re.DOTALL)

templates = OrderedDict(re.findall(r'''
                          ^\|         # \はエスケープ処理、非キャプチャ
                          (.+?)       # キャプチャ対象(key)、非貪欲
                          \s*         # 空白文字0文字以上
                          =           # 検索語句、非キャプチャ
                          \s*         # 空白文字0文字以上
                          (.+?)       # キャプチャ対象(Value)、非貪欲
                          (?:         # キャプチャ対象外のグループ開始
                            (?=\n\|)  # 改行(\n)+'|'の手前(肯定の先読み)
                          | (?=\n$)   # または、改行(\n)+終端の手前(肯定の先読み)
                          )           # キャプチャ対象外のグループ終了
                         ''', basic.group(1), re.MULTILINE+re.VERBOSE+re.DOTALL))

# マークアップ除去
def remove_markup(string):
    
    # 強調マークアップの除去 
    # 除去対象：''他との区別''、'''強調'''、'''''斜体と強調'''''
    replaced = re.sub(r'''
                       (\'{2,5})   # 2〜5個の'（マークアップの開始）
                       (.*?)       # 任意の1文字以上（対象の文字列）
                       (\1)        # 1番目のキャプチャと同じ（マークアップの終了）
                       ''', r'\2', string, flags=re.MULTILINE+re.VERBOSE)

    # 内部リンク・ファイルの除去 
    # 除去対象：[[記事名]]、[[記事名|表示文字]]、[[記事名#節名|表示文字]]、[[ファイル:Wi.png|thumb|説明文]]
    replaced = re.sub(r'''
        \[\[             # '[['(マークアップ開始)
        (?:              # キャプチャ対象外のグループ開始
            [^|]*?       # '|'以外の文字0文字以上、非貪欲
            \|           # '|'
        )*?              # グループ終了、このグループが0以上出現、非貪欲(No27との変更点)
        (                # グループ開始、キャプチャ対象
          (?!Category:)  # 否定の先読(含んだ場合は対象外としている)
          ([^|]*?)    # '|'以外が0文字以上、非貪欲(表示対象の文字列)
        )
        \]\]        # ']]'（マークアップ終了）        
        ''', r'\1', replaced, flags=re.MULTILINE+re.VERBOSE)

    # Template:Langの除去 
    # 除去対象：{{lang|言語タグ|文字列}}
    replaced = re.sub(r'''
        \{\{lang    # '{{lang'(マークアップ開始)
        (?:         # キャプチャ対象外のグループ開始
            [^|]*?  # '|'以外の文字が0文字以上、非貪欲
            \|      # '|'
        )*?         # グループ終了、このグループが0以上出現、非貪欲
        ([^|]*?)    # キャプチャ対象、'|'以外が0文字以上、非貪欲(表示対象の文字列)
        \}\}        # '}}'(マークアップ終了)
        ''', r'\1', replaced, flags=re.MULTILINE+re.VERBOSE)
    
    # 外部リンクの除去
    # 除去対象[http(s)://xxxx] 、[http(s)://xxx xxx]
    replaced = re.sub(r'''
        \[https?:// # '[http://'(マークアップ開始)
        (?:           # キャプチャ対象外のグループ開始
            [^\s]*? # 空白以外の文字が0文字以上、非貪欲
            \s      # 空白
        )?          # グループ終了、このグループが0か1出現
        ([^]]*?)    # キャプチャ対象、']'以外が0文字以上、非貪欲（表示対象の文字列）
        \]          # ']'(マークアップの終了)
        ''', r'\1', replaced, flags=re.MULTILINE+re.VERBOSE)

    # HTMLタグの除去
    # 除去対象 <xx> </xx> <xx/>
    replaced = re.sub(r'''
        <           # '<'(マークアップの開始)
        .+?         # 1文字以上、非貪欲
        >           # '>'(マークアップの終了)
        ''', '', replaced, flags=re.MULTILINE+re.VERBOSE)

    return replaced

for i, (key, value) in enumerate(templates.items()):
    replaced = remove_markup(value)
    templates[key] = replaced
    
    # 変わったものを表示
    if value != replaced:
        print(i, key) 
        print('変更前\t', value)
        print('変更後\t', replaced)
        print('----')

pprint(templates)
```

## 回答解説
### 「ファイル」除去
前回ノックの「内部リンク」除去とほぼ同じなので、該当部分正規表現に修正をしています。
具体的には下記の1つ目のグループの終わりを`)??`から`)*?`へと変更しています。ファイルの場合は`[[ファイル:Wikipedia-logo-v2-ja.png|thumb|説明文]]`と`|`が2回以上登場するので、0回以上にしています。


```python:
replaced = re.sub(r'''
    \[\[             # '[['(マークアップ開始)
    (?:              # キャプチャ対象外のグループ開始
        [^|]*?       # '|'以外の文字0文字以上、非貪欲
        \|           # '|'
    )*?              # グループ終了、このグループが0以上出現、非貪欲(No27との変更点)
    (                # グループ開始、キャプチャ対象
      (?!Category:)  # 否定の先読(含んだ場合は対象外としている)
      ([^|]*?)    # '|'以外が0文字以上、非貪欲(表示対象の文字列)
    )
    \]\]        # ']]'（マークアップ終了）        
    ''', r'\1', replaced, flags=re.MULTILINE+re.VERBOSE)
```

以下がファイル除去の変更前と変更後です。

```
4 国章画像
変更前	 [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
変更後	 イギリスの国章
```

### 「Template:Lang」除去
`{{lang|言語タグ|文字列}}`形式を「文字列」のみに置換しています。

```python:
# 除去対象：{{lang|言語タグ|文字列}}
replaced = re.sub(r'''
    \{\{lang    # '{{lang'(マークアップ開始)
    (?:         # キャプチャ対象外のグループ開始
        [^|]*?  # '|'以外の文字が0文字以上、非貪欲
        \|      # '|'
    )*?         # グループ終了、このグループが0以上出現、非貪欲
    ([^|]*?)    # キャプチャ対象、'|'以外が0文字以上、非貪欲(表示対象の文字列)
    \}\}        # '}}'(マークアップ終了)
    ''', r'\1', replaced, flags=re.MULTILINE+re.VERBOSE)
```

以下が「Template:Lang」除去結果です。

```
2 公式国名
変更前	 {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br/>
*{{lang|gd|An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath}}（[[スコットランド・ゲール語]]）<br/>
*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}（[[ウェールズ語]]）<br/>
*{{lang|ga|Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann}}（[[アイルランド語]]）<br/>
*{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh}}（[[コーンウォール語]]）<br/>
*{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}（[[スコットランド語]]）<br/>
**{{lang|sco|Claught Kängrick o Docht Brätain an Norlin Airlann}}、{{lang|sco|Unitet Kängdom o Great Brittain an Norlin Airlann}}（アルスター・スコットランド語）</ref>
変更後	 United Kingdom of Great Britain and Northern Ireland英語以外での正式国名:
*An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath（スコットランド・ゲール語）
*Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon（ウェールズ語）
*Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann（アイルランド語）
*An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh（コーンウォール語）
*Unitit Kinrick o Great Breetain an Northren Ireland（スコットランド語）
**Claught Kängrick o Docht Brätain an Norlin Airlann、Unitet Kängdom o Great Brittain an Norlin Airlann（アルスター・スコットランド語）
----
6 標語
変更前	 {{lang|fr|Dieu et mon droit}}<br/>（[[フランス語]]:神と私の権利）
変更後	 Dieu et mon droit（フランス語:神と私の権利）
```

### 「外部リンク」除去
httpsも含めて除去しています。

```python:
# 除去対象[http(s)://xxxx] 、[http(s)://xxx xxx]
replaced = re.sub(r'''
    \[https?:// # '[http://'(マークアップ開始)
    (?:           # キャプチャ対象外のグループ開始
        [^\s]*? # 空白以外の文字が0文字以上、非貪欲
        \s      # 空白
    )?          # グループ終了、このグループが0か1出現
    ([^]]*?)    # キャプチャ対象、']'以外が0文字以上、非貪欲（表示対象の文字列）
    \]          # ']'(マークアップの終了)
    ''', r'\1', replaced, flags=re.MULTILINE+re.VERBOSE)
```

以下が外部リンク除去部分です。

```
23 人口値
変更前	 63,181,775<ref>[http://esa.un.org/unpd/wpp/Excel-Data/population.htm United Nations Department of Economic and Social Affairs>Population Division>Data>Population>Total Population]</ref>
変更後	 63,181,775United Nations Department of Economic and Social Affairs>Population Division>Data>Population>Total Population
----
26 GDP値元
変更前	 1兆5478億<ref name="imf-statistics-gdp">[http://www.imf.org/external/pubs/ft/weo/2012/02/weodata/weorept.aspx?pr.x=70&pr.y=13&sy=2010&ey=2012&scsm=1&ssd=1&sort=country&ds=.&br=1&c=112&s=NGDP%2CNGDPD%2CPPPGDP%2CPPPPC&grp=0&a= IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom]</ref>
変更後	 1兆5478億IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom
```

### 「HTMLタグ」除去
HTMLタグも除去しています。「`<`と`>`に囲まれていたら」という単純な正規表現。

```python:
# 除去対象 <xx> </xx> <xx/>
replaced = re.sub(r'''
    <           # '<'(マークアップの開始)
    .+?         # 1文字以上、非貪欲
    >           # '>'(マークアップの終了)
    ''', '', replaced, flags=re.MULTILINE+re.VERBOSE)
```

以下が結果です。結構多いですね。

```
2 公式国名
変更前	 {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br/>
*{{lang|gd|An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath}}（[[スコットランド・ゲール語]]）<br/>
*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}（[[ウェールズ語]]）<br/>
*{{lang|ga|Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann}}（[[アイルランド語]]）<br/>
*{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh}}（[[コーンウォール語]]）<br/>
*{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}（[[スコットランド語]]）<br/>
**{{lang|sco|Claught Kängrick o Docht Brätain an Norlin Airlann}}、{{lang|sco|Unitet Kängdom o Great Brittain an Norlin Airlann}}（アルスター・スコットランド語）</ref>
変更後	 United Kingdom of Great Britain and Northern Ireland英語以外での正式国名:
*An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath（スコットランド・ゲール語）
*Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon（ウェールズ語）
*Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann（アイルランド語）
*An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh（コーンウォール語）
*Unitit Kinrick o Great Breetain an Northren Ireland（スコットランド語）
**Claught Kängrick o Docht Brätain an Norlin Airlann、Unitet Kängdom o Great Brittain an Norlin Airlann（アルスター・スコットランド語）
----
6 標語
変更前	 {{lang|fr|Dieu et mon droit}}<br/>（[[フランス語]]:神と私の権利）
変更後	 Dieu et mon droit（フランス語:神と私の権利）
----
23 人口値
変更前	 63,181,775<ref>[http://esa.un.org/unpd/wpp/Excel-Data/population.htm United Nations Department of Economic and Social Affairs>Population Division>Data>Population>Total Population]</ref>
変更後	 63,181,775United Nations Department of Economic and Social Affairs>Population Division>Data>Population>Total Population
----
26 GDP値元
変更前	 1兆5478億<ref name="imf-statistics-gdp">[http://www.imf.org/external/pubs/ft/weo/2012/02/weodata/weorept.aspx?pr.x=70&pr.y=13&sy=2010&ey=2012&scsm=1&ssd=1&sort=country&ds=.&br=1&c=112&s=NGDP%2CNGDPD%2CPPPGDP%2CPPPPC&grp=0&a= IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom]</ref>
変更後	 1兆5478億IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom
----
29 GDP値MER
変更前	 2兆4337億<ref name="imf-statistics-gdp" />
変更後	 2兆4337億
----
32 GDP値
変更前	 2兆3162億<ref name="imf-statistics-gdp" />
変更後	 2兆3162億
----
33 GDP/人
変更前	 36,727<ref name="imf-statistics-gdp" />
変更後	 36,727
----
37 確立形態2
変更前	 [[グレートブリテン王国]]建国<br />（[[連合法 (1707年)|1707年連合法]]）
変更後	 グレートブリテン王国建国（1707年連合法）
----
39 確立形態3
変更前	 [[グレートブリテン及びアイルランド連合王国]]建国<br />（[[連合法 (1800年)|1800年連合法]]）
変更後	 グレートブリテン及びアイルランド連合王国建国（1800年連合法）
----
48 ccTLD
変更前	 [[.uk]] / [[.gb]]<ref>使用は.ukに比べ圧倒的少数。</ref>
変更後	 .uk / .gb使用は.ukに比べ圧倒的少数。
----
50 注記
変更前	 <references />
変更後	 
```

### 出力結果(実行結果)
プログラム実行すると最後に以下の結果が出力されます。すっきりしました。

```bash:出力結果
OrderedDict([('略名', 'イギリス'),
             ('日本語国名', 'グレートブリテン及び北アイルランド連合王国'),
             ('公式国名',
              'United Kingdom of Great Britain and Northern '
              'Ireland英語以外での正式国名:\n'
              '*An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu '
              'Thuath（スコットランド・ゲール語）\n'
              '*Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon（ウェールズ語）\n'
              '*Ríocht Aontaithe na Breataine Móire agus Tuaisceart na '
              'hÉireann（アイルランド語）\n'
              '*An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh（コーンウォール語）\n'
              '*Unitit Kinrick o Great Breetain an Northren Ireland（スコットランド語）\n'
              '**Claught Kängrick o Docht Brätain an Norlin Airlann、Unitet '
              'Kängdom o Great Brittain an Norlin Airlann（アルスター・スコットランド語）'),
             ('国旗画像', 'Flag of the United Kingdom.svg'),
             ('国章画像', 'イギリスの国章'),
             ('国章リンク', '（国章）'),
             ('標語', 'Dieu et mon droit（フランス語:神と私の権利）'),
             ('国歌', '神よ女王陛下を守り給え'),
             ('位置画像', 'Location_UK_EU_Europe_001.svg'),
             ('公用語', '英語（事実上）'),
             ('首都', 'ロンドン'),
             ('最大都市', 'ロンドン'),
             ('元首等肩書', '女王'),
             ('元首等氏名', 'エリザベス2世'),
             ('首相等肩書', '首相'),
             ('首相等氏名', 'デーヴィッド・キャメロン'),
             ('面積順位', '76'),
             ('面積大きさ', '1 E11'),
             ('面積値', '244,820'),
             ('水面積率', '1.3%'),
             ('人口統計年', '2011'),
             ('人口順位', '22'),
             ('人口大きさ', '1 E7'),
             ('人口値',
              '63,181,775United Nations Department of Economic and Social '
              'Affairs>Population Division>Data>Population>Total Population'),
             ('人口密度値', '246'),
             ('GDP統計年元', '2012'),
             ('GDP値元',
              '1兆5478億IMF>Data and Statistics>World Economic Outlook '
              'Databases>By Countrise>United Kingdom'),
             ('GDP統計年MER', '2012'),
             ('GDP順位MER', '5'),
             ('GDP値MER', '2兆4337億'),
             ('GDP統計年', '2012'),
             ('GDP順位', '6'),
             ('GDP値', '2兆3162億'),
             ('GDP/人', '36,727'),
             ('建国形態', '建国'),
             ('確立形態1', 'イングランド王国／スコットランド王国（両国とも1707年連合法まで）'),
             ('確立年月日1', '927年／843年'),
             ('確立形態2', 'グレートブリテン王国建国（1707年連合法）'),
             ('確立年月日2', '1707年'),
             ('確立形態3', 'グレートブリテン及びアイルランド連合王国建国（1800年連合法）'),
             ('確立年月日3', '1801年'),
             ('確立形態4', '現在の国号「グレートブリテン及び北アイルランド連合王国」に変更'),
             ('確立年月日4', '1927年'),
             ('通貨', 'UKポンド (&pound;)'),
             ('通貨コード', 'GBP'),
             ('時間帯', '±0'),
             ('夏時間', '+1'),
             ('ISO 3166-1', 'GB / GBR'),
             ('ccTLD', '.uk / .gb使用は.ukに比べ圧倒的少数。'),
             ('国際電話番号', '44'),
             ('注記', '')])
```