0
0

言語処理100本ノック2020 (20~29)

Last updated at Posted at 2023-10-21

はじめに

本記事は言語処理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

コメント
ロジックを考える系の問題では無いです。だから、先駆者の解答と似たり寄ったりになりますね。

他章の解答例

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0