schedule2020-03-17

27. 内部リンクの除去

はじめに

言語処理100本ノック 2015

Pythonを勉強するため、東京工業大学の岡崎教授が出題されている言語処理100本ノック 2015を解いていきます。

環境

転職してちょうど1年空いてしまった。 やる気が出たので続きをしていきます。

この先、環境がばらつくかもしれませんがご了承ください。

  • Python 3.7 (← Python3.6)
  • OS : Ubuntu 18 (← Linux Mint)

問題

3章からWikiを解析する問題になっています。

Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある.

  • 1行に1記事の情報がJSON形式で格納される
  • 各行には記事名が"title"キーに,記事本文が"text"キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
  • ファイル全体はgzipで圧縮される
    以下の処理を行うプログラムを作成せよ.

27. 内部リンクの除去
26の処理に加えて,テンプレートの値からMediaWikiの内部リンクマークアップを除去し,テキストに変換せよ(参考: マークアップ早見表).

Point

  • ファイルの読み込み、イギリスの記事抽出は20と同じ。
  • テンプレートを記述している箇所の抽出は25と同じ。
  • 強調マークアップの除去は26と同じ。
  • 内部リンクの除去を行う。

内部リンクのマークアップは以下の通り。

入力内容 表示結果
[[記事名]] 記事名
`[[記事名 表示文字]]`
`[[記事名#節名 表示文字]]`

入力内容から表示結果の文字列に変換することを、内部リンクの除去とします。 [[]]で囲まれた箇所を|が無ければそのまま、|があれば右側の文字列を残すようにします。

例えば、前回の出力の中では以下の箇所が該当します。

# [[記事名]]
[[イングランド王国]] -> イングランド

# [[記事名|表示文字]]
[[スターリング・ポンド|UKポンド]] -> UKポンド

解答:Python

# coding: utf-8

import gzip
import json
import re


def read_wiki(fname, tiltle):
    with gzip.open(fname, 'rt', encoding="utf-8") as data_file:
        for line in data_file:
            data_json = json.loads(line)
            if data_json['title'] == 'イギリス':
                return data_json['text']


def get_basic_information(string):
    m1 = re.search(r'{{基礎情報 国.*?', string)
    # print(m1.end())
    m2 = re.search(r'(.*)\n}}\n', string[m1.end():])
    return string[m1.end():m2.end()+1]


def remove_emphasis(string):
    # 強調を除去
    # m = re.match(r"(.*)('{2,5})(.*?)(\2)(.*)", string) 何故か{2,5}で'がひとつ残る
    m = re.match(r"(.*)('{3,5})(.*?)(\2)(.*)", string)
    if m is None:
        return string
    dst = m.group(1) + m.group(3) + m.group(5)
    # print(dst)
    return dst


def remove_inner_link(string):
    # 内部リンクを除去
    dst = re.sub(r"\[\[(?:[^:\]]+?\|)?([^:]+?)\]\]", r"\1", string)
    return dst


def main():
    fname = 'jawiki-country.json.gz'
    text = read_wiki(fname, 'イギリス')
    # print(len(text))
    text = get_basic_information(text).split('\n')

    basic_info = {}
    for line in text:
        m = re.match(r'\|(.+)( = )(.*)', line)
        if m is None:
            continue
        val = remove_emphasis(m.group(3).strip())
        val = remove_inner_link(val)
        basic_info[m.group(1).strip()] = val

    # JSONで表示を整形
    print(json.dumps(basic_info, sort_keys=True, indent=4, ensure_ascii=False))


if __name__ == '__main__':
    main()

解説

カッコ内に|がある場合に右側だけ取り出す方法が判らず、こちらの方法をお借りしました。

また、今までre.matchを利用していましたが一行に内部リンクが複数ある場合に対応できなかったため、re.subにしました。

re.sub(pattern, repl, string, count=0, flags=0)

re.subはstring中に出現する最も左の重複しない pattern を置換 repl で置換することで得られる文字列を返します。 countが0のときマッチする箇所を全て置換します。

>> print(remove_inner_link(
    '[[グレートブリテン及びアイルランド連合王国]]建国<br />([[連合法 (1800年)|1800年連合法]])'))
グレートブリテン及びアイルランド連合王国建国<br />1800年連合法)

>> print(remove_inner_link('[[エリザベス2世]]'))
エリザベス2

出力

{
    "GDP/人": "36,727<ref name=\"imf-statistics-gdp\" />",
    "GDP値": "2兆3162億<ref name=\"imf-statistics-gdp\" />",
    "GDP値MER": "2兆4337億<ref name=\"imf-statistics-gdp\" />",
    "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統計年": "2012",
    "GDP統計年MER": "2012",
    "GDP統計年元": "2012",
    "GDP順位": "6",
    "GDP順位MER": "5",
    "ISO 3166-1": "GB / GBR",
    "ccTLD": ".uk / .gb<ref>使用は.ukに比べ圧倒的少数。</ref>",
    "人口値": "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>",
    "人口大きさ": "1 E7",
    "人口密度値": "246",
    "人口統計年": "2011",
    "人口順位": "22",
    "位置画像": "Location_UK_EU_Europe_001.svg",
    "元首等氏名": "エリザベス2世",
    "元首等肩書": "女王",
    "公式国名": "{{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br/>",
    "公用語": "英語(事実上)",
    "国旗画像": "Flag of the United Kingdom.svg",
    "国歌": "神よ女王陛下を守り給え",
    "国章リンク": "(国章)",
    "国章画像": "[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]",
    "国際電話番号": "44",
    "夏時間": "+1",
    "建国形態": "建国",
    "日本語国名": "グレートブリテン及び北アイルランド連合王国",
    "時間帯": "±0",
    "最大都市": "ロンドン",
    "標語": "{{lang|fr|Dieu et mon droit}}<br/>(フランス語:神と私の権利)",
    "水面積率": "1.3%",
    "略名": "イギリス",
    "確立年月日1": "927年/843年",
    "確立年月日2": "1707年",
    "確立年月日3": "1801年",
    "確立年月日4": "1927年",
    "確立形態1": "イングランド王国/スコットランド王国<br />(両国とも1707年連合法まで)",
    "確立形態2": "グレートブリテン王国建国<br />(1707年連合法)",
    "確立形態3": "グレートブリテン及びアイルランド連合王国建国<br />(1800年連合法)",
    "確立形態4": "現在の国号「グレートブリテン及び北アイルランド連合王国」に変更",
    "通貨": "UKポンド (&pound;)",
    "通貨コード": "GBP",
    "面積値": "244,820",
    "面積大きさ": "1 E11",
    "面積順位": "76",
    "首相等氏名": "デーヴィッド・キャメロン",
    "首相等肩書": "首相",
    "首都": "ロンドン"
}

次の記事

Python3で言語処理100本ノックまとめ

前の問題:26. 強調マークアップの除去

前の問題:28