schedule2019-03-14

26. 強調マークアップの除去

はじめに

言語処理100本ノック 2015

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

より深く理解するため、別解や利用したライブラリの解説もまとめていきます。

環境

  • Python3.6
  • OS : Linux Mint

OSがMacからLinux Mintに変わりました。

問題

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

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

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

26. 強調マークアップの除去
25の処理時に,テンプレートの値からMediaWikiの強調マークアップ(弱い強調,強調,強い強調のすべて)を除去してテキストに変換せよ(参考: マークアップ早見表).

Point

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

強調マークアップは以下の通り。

種類 入力内容 表示結果
他との区別(斜体) ''他との区別'' 他との区別
強調(太字) '''強調''' 強調
斜体と強調 '''''斜体と強調''''' 斜体と強調

'が2、3,5個で囲まれた箇所の中身を取り出せば良さそうです。 4個がないのは不思議ですが、2~5個の'に囲まれた箇所を見つけます。

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

'確立形態4': "現在の国号「'''グレートブリテン及び北アイルランド連合王国'''」に変更",

解答:Python

前後同じ個数の繰り返し文字を検出するにはr"('{2,5})(.*?)(\1)"とします。

{min,max}は直前の文字のmin以上、max以下の繰り返しを抽出。 {min,max}が最長一致、{min,max}?が最短一致となる。

(\n)はn番目の括弧と同じ文字を抽出する。 r"('{2,5})(.*?)(\1)"の場合、1番目の'と同じ数の'を抽出できる。

Source code

# coding: utf-8

import gzip
import json
import re


def read_wiki(fname, tiltle):
    with gzip.open(fname, 'rt') 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 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())
        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()

出力

テンプレートを抽出して強調を削除しています。 ※表示は見やすくJSONで整えました。

$ python 26.\ 強調マークアップの除去.py
{
    "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年)|1707年連合法]]まで)",
    "確立形態2": "[[グレートブリテン王国]]建国<br />([[連合法 (1707年)|1707年連合法]])",
    "確立形態3": "[[グレートブリテン及びアイルランド連合王国]]建国<br />([[連合法 (1800年)|1800年連合法]])",
    "確立形態4": "現在の国号「グレートブリテン及び北アイルランド連合王国」に変更",
    "通貨": "[[スターリング・ポンド|UKポンド]] (&pound;)",
    "通貨コード": "GBP",
    "面積値": "244,820",
    "面積大きさ": "1 E11",
    "面積順位": "76",
    "首相等氏名": "[[デーヴィッド・キャメロン]]",
    "首相等肩書": "[[イギリスの首相|首相]]",
    "首都": "[[ロンドン]]"
}
&quot;確立形態4&quot;: &quot;現在の国号「グレートブリテン及び北アイルランド連合王国」に変更&quot;,

強調マークアップを除けました。

コメントにあるように何故か{2,5}'がひとつ残る。 '''文字'''-> '文字'となる。

  • {2,5}が最長一致
  • {2,5}?が最短一致

となっているはずなのに・・・

参考:正規表現一覧

次の記事

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

前の問題:25. テンプレートの抽出

前の問題:27. 内部リンクの除去