言語処理100本ノック-27:内部リンクの除去

[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)[「第3章: 正規表現」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch3)の[27本目「内部リンクの除去」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec27)記録です。
ここまで来ると今まで学んだ正規表現の内容を組み合わせるだけです。私が慣れないからなのか、それでも試行錯誤に時間がかかります。

# 参考リンク

|リンク|備考|
|:--|:--|
|[027.内部リンクの除去.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/03.%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE/027.%E5%86%85%E9%83%A8%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%AE%E9%99%A4%E5%8E%BB.ipynb)|回答プログラムのGitHubリンク|
|[素人の言語処理100本ノック:27](https://qiita.com/segavvy/items/9a8137f045852bc299d6)|多くのソース部分のコピペ元|
|[ゼロから覚える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で圧縮される

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

### [27. 内部リンクの除去](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec27)
> 26の処理に加えて，テンプレートの値からMediaWikiの内部リンクマークアップを除去し，テキストに変換せよ（参考: [マークアップ早見表](http://ja.wikipedia.org/wiki/Help:%E6%97%A9%E8%A6%8B%E8%A1%A8)）．

### 課題補足(「内部リンク」について)
[Help:早見表](https://ja.wikipedia.org/wiki/Help:%E6%97%A9%E8%A6%8B%E8%A1%A8)によると「内部リンク」は以下の3種類の書式です。

- `[[記事名]]`
- `[[記事名|表示文字]]`
- `[[記事名#節名|表示文字]]`

少し長いですが、以下の文字列を変更します。

```
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>
変更後	 {{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>
----
5 国章リンク
変更前	 （[[イギリスの国章|国章]]）
変更後	 （国章）
----
6 標語
変更前	 {{lang|fr|Dieu et mon droit}}<br/>（[[フランス語]]:神と私の権利）
変更後	 {{lang|fr|Dieu et mon droit}}<br/>（フランス語:神と私の権利）
----
7 国歌
変更前	 [[女王陛下万歳|神よ女王陛下を守り給え]]
変更後	 神よ女王陛下を守り給え
----
9 公用語
変更前	 [[英語]]（事実上）
変更後	 英語（事実上）
----
10 首都
変更前	 [[ロンドン]]
変更後	 ロンドン
----
12 元首等肩書
変更前	 [[イギリスの君主|女王]]
変更後	 女王
----
13 元首等氏名
変更前	 [[エリザベス2世]]
変更後	 エリザベス2世
----
14 首相等肩書
変更前	 [[イギリスの首相|首相]]
変更後	 首相
----
15 首相等氏名
変更前	 [[デーヴィッド・キャメロン]]
変更後	 デーヴィッド・キャメロン
----
35 確立形態1
変更前	 [[イングランド王国]]／[[スコットランド王国]]<br />（両国とも[[連合法 (1707年)|1707年連合法]]まで）
変更後	 イングランド王国／スコットランド王国<br />（両国とも1707年連合法まで）
----
36 確立年月日1
変更前	 [[927年]]／[[843年]]
変更後	 927年／843年
----
37 確立形態2
変更前	 [[グレートブリテン王国]]建国<br />（[[連合法 (1707年)|1707年連合法]]）
変更後	 グレートブリテン王国建国<br />（1707年連合法）
----
38 確立年月日2
変更前	 [[1707年]]
変更後	 1707年
----
39 確立形態3
変更前	 [[グレートブリテン及びアイルランド連合王国]]建国<br />（[[連合法 (1800年)|1800年連合法]]）
変更後	 グレートブリテン及びアイルランド連合王国建国<br />（1800年連合法）
----
40 確立年月日3
変更前	 [[1801年]]
変更後	 1801年
----
42 確立年月日4
変更前	 [[1927年]]
変更後	 1927年
----
43 通貨
変更前	 [[スターリング・ポンド|UKポンド]] (&pound;)
変更後	 UKポンド (&pound;)
----
48 ccTLD
変更前	 [[.uk]] / [[.gb]]<ref>使用は.ukに比べ圧倒的少数。</ref>
変更後	 .uk / .gb<ref>使用は.ukに比べ圧倒的少数。</ref>
```

# 回答
## 回答プログラム [027.内部リンクの除去.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/03.%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE/027.%E5%86%85%E9%83%A8%E3%83%AA%E3%83%B3%E3%82%AF%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)

    # 内部リンクの除去
    replaced = re.sub(r'''
        \[\[             # '[['(マークアップ開始)
        (?:              # キャプチャ対象外のグループ開始
            [^|]*?       # '|'以外の文字0文字以上、非貪欲
            \|           # '|'
        )??              # グループ終了、このグループが0か1出現、非貪欲
        (                # グループ開始、キャプチャ対象
          (?!Category:)  # 否定の先読(含んだ場合は対象外としている)
          ([^|]*?)    # '|'以外が0文字以上、非貪欲(表示対象の文字列)
        )
        \]\]        # ']]'（マークアップ終了）
        ''', 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)```
```

## 回答解説
前回ノックで作成した関数`remove_markup`に以下の内部リンク除去を追加しています。`Category:`の部分は、今回の対処範囲に(偶然？)含んでいないのですが、もしあったらば巻き込んでしまうので追加しておきました。
以下の3パターンでパターン1の場合は記事名をキャプチャ、他2パターンは表示文字をキャプチャします。

1. `[[記事名]]`
2. `[[記事名|表示文字]]`
3. `[[記事名#節名|表示文字]]`


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

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

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