はじめに
本記事は言語処理100本ノックの解説です。
100本のノックを全てこなした記録をQiitaに残します。
使用言語はPythonです。
今回は第3章: 正規表現(20~29)までの解答例をご紹介します。
第3章: 正規表現
Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある.・1行に1記事の情報がJSON形式で格納される
・各行には記事名が”title”キーに,記事本文が”text”キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
・ファイル全体はgzipで圧縮される
以下の処理を行うプログラムを作成せよ.
私の観測している限り、3章で100本ノックを辞める人が多い印象です。
本記事が3章を突破するヒントになれば幸いです。
20. JSONデータの読み込み
Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題21-29では,ここで抽出した記事本文に対して実行せよ.
import json
import re
text_list = []
basepath = "[PATH]"
with open('{}/jawiki-country.json'.format(basepath), 'r') as f:
lines = f.readlines()
for line in lines:
text_list.append(json.loads(line))
for i in range(len(text_list)):
if text_list[i]['title']=="イギリス":
UK_text = str(text_list[i])
break
#問題21-25でUK_textを使います
UK_text
{\'title\': \'イギリス\', \'text\': \'{{redirect|UK}}\\n{{redirect|英国|春秋時代の諸侯国|英 (春秋)}}\\n{{Otheruses|ヨーロッパの国|長崎県・熊本県の郷土料理|いぎりす}}\\n{{基礎情報 国\\n|略名 =イギリス\\n|日本語国名 = グレートブリテン及び北アイルランド連合王国\\n|公式国名 = {{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}}([[スコットランド・ゲール語]])\\n*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}([[ウェールズ語]])\\n*{{lang|ga|Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann}}([[アイルランド語]])\\n*{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh}}([[コーンウォール語]])\\n*{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}([[スコットランド語]])\\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>\\n|国旗画像 = Flag of the United Kingdom.svg\\n|国章画像 = [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]\\n|国章リンク =([[イギリスの国章|国章 ~~~省略
コメント
問題21-25で本問の変数「UK_text」を使います。
21. カテゴリ名を含む行を抽出
記事中でカテゴリ名を宣言している行を抽出せよ.
import re
pattern = "\[\[Category:.*?\]\]"
result = re.findall(pattern, UK_text)
result
['[[Category:イギリス|*]]',
'[[Category:イギリス連邦加盟国]]',
'[[Category:英連邦王国|*]]',
'[[Category:G8加盟国]]',
'[[Category:欧州連合加盟国|元]]',
'[[Category:海洋国家]]',
'[[Category:現存する君主国]]',
'[[Category:島国]]',
'[[Category:1801年に成立した国家・領域]]']
コメント
正規表現はたった1行を書くために長時間考えますよね。
22. カテゴリ名の抽出
記事のカテゴリ名を(行単位ではなく名前で)抽出せよ.
import re
pattern = "\[\[Category:(.*?)(?:\|.*?|)\]\]"
result = re.findall(pattern, UK_text)
result
['イギリス',
'イギリス連邦加盟国',
'英連邦王国',
'G8加盟国',
'欧州連合加盟国',
'海洋国家',
'現存する君主国',
'島国',
'1801年に成立した国家・領域']
コメント
正規表現を秒で書ける人の頭の中が見たい。
23. セクション構造
記事中に含まれるセクション名とそのレベル(例えば”== セクション名 ==”なら1)を表示せよ.
import collections
import re
pattern = "(={2,4}.*?={2,4})"
result = re.findall(pattern, UK_text)
section = {}
for text in result:
c1 = collections.Counter(text)
c2 = int(c1['=']/2) - 1
text = text.replace('=', '')
section[text] = c2
section
{'国名': 1,
'歴史': 1,
'地理': 1,
'主要都市': 2,
'気候': 2,
'政治': 1,
'元首': 2,
'法': 2,
'内政': 2,
'地方行政区分': 2,
'外交・軍事': 2,
~~~~~~~~~~~~省略~~~~~~~~~~~~
コメント
だいぶ前に書いたコードなんですけど、「int(c1['=']/2) - 1」の部分がかっこいい処理だなと自画自賛(すみません)。
24. ファイル参照の抽出
記事から参照されているメディアファイルをすべて抜き出せ.
import re
pattern = '\[\[ファイル:(.*?)(?:\||\])'
result = re.findall(pattern, UK_text)
result
['Royal Coat of Arms of the United Kingdom.svg',
'United States Navy Band - God Save the Queen.ogg',
'Descriptio Prime Tabulae Europae.jpg',
"Lenepveu, Jeanne d\\'Arc au siège d\\'Orléans.jpg",
'London.bankofengland.arp.jpg',
~~~~~~~~~~~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~~~~~~~
コメント
wikipediaの画像は誰があげているんですかね。「a.jpeg」とかにしていないのは好感。
25. テンプレートの抽出
記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ.
import re
pattern = '基礎情報(.*?\<references/\>)'
result = re.findall(pattern, UK_text)
result[0] += "\\n"
pattern = '(?<=\\\\n\|)(.*?) *= *(.*?)(?=\\\\n)'
result2 = re.findall(pattern, result[0])
inf_dic = {}
for i, j in result2:
inf_dic[i] = j
inf_dic
{'略名': 'イギリス',
'日本語国名': 'グレートブリテン及び北アイルランド連合王国',
'公式国名': '{{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|イギリスの国章]]',
~~~~~~~~~~~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~~~~~~~
コメント
正規表現あるあるだと思うけど、答えがあっているか確認が大変。例外的なパターンがあって取りこぼしているかもしれないです。
「inf_dic」は次問で使います。
26. 強調マークアップの除去
25の処理時に,テンプレートの値からMediaWikiの強調マークアップ(弱い強調,強調,強い強調のすべて)を除去してテキストに変換せよ(参考: マークアップ早見表).
import re
inf_dic2 = {}
for key, text in inf_dic.items():
inf_dic2[key] = re.sub(r'(\\\'){2,5}' , '', text)
inf_dic2
#処理前
'国歌': "[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />\\'\\'神よ女王を護り賜え\\'\\'<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}",
#処理後
'国歌': '[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />神よ女王を護り賜え<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}',
コメント
強調マークアップが少なかったので変わっているところだけ掲載。「inf_dic2」は次問で使います。
27. 内部リンクの除去
26の処理に加えて,テンプレートの値からMediaWikiの内部リンクマークアップを除去し,テキストに変換せよ(参考: マークアップ早見表).
import re
inf_dic3 = {}
for key, text in inf_dic2.items():
pattern = "(?<=\}\}\<br \/\>()\[{2}"#<br />(フランス語]]:[[Dieu et mon droit|神と我が権利]])
text = re.sub(pattern, '', text)
pattern = "\[{2}.*?\|.*?px\|(?=.*?\]\])"#'[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]',
text = re.sub(pattern, '', text)
pattern = "(?<=(\|))\[{2}"
text = re.sub(pattern, '', text)
pattern = "(?<=\}{2}()\[{2}"#スコットランド・ゲール語]])\n*{{lang|cy
text = re.sub(pattern, '', text)
pattern = "(?<=\>()\[{2}.*?\|"#[[グレートブリテン及びアイルランド連合王国]]成立<br />(1800年合同法]])
text = re.sub(pattern, '', text)
pattern = "(?<=(.{4}).*?\[{2}.*?\)\|" #'[[イングランド王国]]/[[スコットランド王国]]<br />(両国とも[[合同法 (1707年)|1707年合同法]]まで)',
text = re.sub(pattern, '', text)
pattern = "\[{2}.*?\|"#[[(除去)|]]の処理
text = re.sub(pattern, '', text)
pattern = "(\[{2}|\]{2})"#最後に残ったやつを処理
inf_dic3[key] = re.sub(pattern, '', text)
inf_dic3
{'略名': 'イギリス',
'日本語国名': 'グレートブリテン及び北アイルランド連合王国',
'公式国名': '{{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />',
'国旗画像': 'Flag of the United Kingdom.svg',
'国章画像': 'イギリスの国章',
'国章リンク': '(国章)',
'標語': '{{lang|fr|Dieu et mon droit}}<br />(フランス語:神と我が権利)',
'国歌': '{{lang|en|God Save the Queen}}{{en icon}}<br />神よ女王を護り賜え<br />{{center|ファイル:United States Navy Band - God Save the Queen.ogg}}',
~~~~~~~~~~~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~~~~~~~
コメント
本問は例外処理を1つ1つ処理できる正規表現を作成。取りこぼしや処理の間違いはあるかもしれないです。他の人はどう解くか気になる問題。
「inf_dic3」は次問で使います。
28. MediaWikiマークアップの除去
27の処理に加えて,テンプレートの値からMediaWikiマークアップを可能な限り除去し,国の基本情報を整形せよ.
inf_dic4 = {}
for key, text in inf_dic3.items():
pattern = '\{\{.*?\{\{center\|' #{{center|ファイル:United States Navy Band - God Save the Queen.ogg}}',
text = re.sub(pattern, '', text)
pattern = '\{\{.*?\|.*?\|.{2}\|'
text = re.sub(pattern, '', text) #'{{仮リンク|リンゼイ・ホイル|en|Lindsay Hoyle}}'
pattern = '\<ref.*?\>.*?\<\/ref\>'#<ref>で囲んでいるもの
text = re.sub(pattern, '', text)
pattern = '\<ref.*?\>|\<br \/\>'#<ref>単体+<br />単体
text = re.sub(pattern, '', text)
pattern = '\{\{lang\|.*?\|'#公式国名、標語の外国語タイトルを消すため
text = re.sub(pattern, '', text)
pattern = '\{\{.*?\}\}'#確立年月日2,3,4の{を除去する
text = re.sub(pattern, '', text)
pattern = '\}\}'# 公式国名、標語、国家、他元首等氏名2の}を除去する
text = re.sub(pattern, '', text)
inf_dic4[key] = text
inf_dic4
{'略名': 'イギリス',
'日本語国名': 'グレートブリテン及び北アイルランド連合王国',
'公式国名': 'United Kingdom of Great Britain and Northern Ireland英語以外での正式国名:',
'国旗画像': 'Flag of the United Kingdom.svg',
'国章画像': 'イギリスの国章',
'国章リンク': '(国章)',
'標語': 'Dieu et mon droit(フランス語:神と我が権利)',
'国歌': 'ファイル:United States Navy Band - God Save the Queen.ogg',
~~~~~~~~~~~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~~~~~~~
コメント
泥臭くやりました。正規表現を適用する順番も重要。解答が割れる問題だと思うので、いろいろな人の解答を見てください。
「inf_dic4」は次問で使います。
29. 国旗画像のURLを取得する
テンプレートの内容を利用し,国旗画像のURLを取得せよ.(ヒント: MediaWiki APIのimageinfoを呼び出して,ファイル参照をURLに変換すればよい)
import urllib
url = 'https://www.mediawiki.org/w/api.php?action=query&titles=File:' + urllib.parse.quote(inf_dic4['国旗画像']) + '&format=json&prop=imageinfo&iiprop=url'
connection = urllib.request.urlopen(urllib.request.Request(url))
response = json.loads(connection.read().decode())
print(response['query']['pages']['-1']['imageinfo'][0]['url'])
https://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_the_United_Kingdom_%283-5%29.svg
下のリンクです。
https://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_the_United_Kingdom_%283-5%29.svg
コメント
ロジックを考える系の問題では無いです。だから、先駆者の解答と似たり寄ったりになりますね。
他章の解答例