言語処理100本ノック 2020 (Rev 2) 第3章: 正規表現 25. テンプレートの抽出
問題
25. テンプレートの抽出
記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ.
import pandas as pd
import re
df_j = pd.read_json('chapter03/jawiki-country.json.gz', lines=True, compression='infer')
text_uk = df_j.query('title=="イギリス"')['text'].values[0]
template_text = re.findall(r'\{\{基礎情報 (.+?^}\})', text_uk, re.MULTILINE+re.DOTALL)[0]
template = dict(re.findall("\|(.+?) *= *(.+?)\n(?=\||})", template_text, re.MULTILINE+re.DOTALL))
for t in template:
print(t, template[t])
略名 イギリス
日本語国名 グレートブリテン及び北アイルランド連合王国
公式国名 {{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}}([[スコットランド・ゲール語]])
*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}([[ウェールズ語]])
この問題では、正規表現を用いて基礎情報[1]に該当する箇所を取り出します。
r'\{\{基礎情報 (.+?^}\})'
: \{
は {
を表すエスケープシーケンスです。2つ並べることで、{{
を表しています。それに続いて、基礎情報
という文字列が続きます。次に、(.+?^}\})
というパターンがあります。これは、正規表現のグループを表す括弧で囲まれています。その中で、.
は任意の1文字を表します。+
は1回以上の繰り返しを意味します。.+
とすることで、任意の1文字以上の連続する文字列を表しています。さらに、.+?
とすることで、非貪欲マッチ (non-greedy match)
を行うことを指定しています。修飾子の後に ?
を追加すると、 非貪欲
(あるいは 最小
と言われてる) のマッチが行われ、できるだけ少ない文字にマッチします。つまり、.+?
の中にある ^
と }}
の間に最短でマッチするようにしています。^
は(キャレット) 文字列の先頭にマッチし、 MULTILINE
モード[2]では各改行の直後にもマッチします。
re.MULTILINE
とあわせて利用しているre.DOTALL
ですが、.
特殊文字を、改行を含むあらゆる文字にマッチさせます。このフラグがなければ、.
は、改行 以外の あらゆる文字とマッチします。
'\|(.+?) *= *(.+?)\n(?=\||})'
: \|
は、|
を表すエスケープシーケンスです。それに続いて、(.+?)
というパターンがあります。これは、先ほどと同様に、任意の1文字以上の連続する文字列を表しています。次に、スペース文字が0個以上続きます ( *= *)
。さらに、次の (.+?)
というパターンがあります。これも、先ほどと同様に、任意の1文字以上の連続する文字列を表しています。最後に、改行文字 (\n)
が続きます。そして、(?=\||})
というパターンがあります。これは、正規表現の lookahead assertion
を表しています。このパターンを使用することで、現在の位置の後ろに、|
または }
のいずれかが存在するかどうかを確認することができます。
参考記事
第3章: 正規表現
re --- 正規表現操作
Python正規表現
regex101
先読みと後読み(Lookahead/lookbehind)
Discussion