Traffine I/O

日本語

2023-02-03

言語処理100本ノック第3章:正規表現

はじめに

言語処理100本ノックというNLPに関する問題集が東京工業大学によって作成、管理されています。

https://nlp100.github.io/ja/ch03.html

この記事では、「第3章: 正規表現」について回答例を紹介します。

環境設定

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

  • 1 行に 1 記事の情報が JSON 形式で格納される
  • 各行には記事名が”title”キーに,記事本文が”text”キーの辞書オブジェクトに格納され,そのオブジェクトが JSON 形式で書き出される
  • ファイル全体は gzip で圧縮される

以下の処理を行うプログラムを作成せよ.

$ wget https://nlp100.github.io/data/jawiki-country.json.gz

20. JSON データの読み込み

Wikipedia 記事の JSON ファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題 21-29 では,ここで抽出した記事本文に対して実行せよ.

import pandas as pd

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]
print(wiki_uk)
{{redirect|UK}}
{{redirect|英国|春秋時代の諸侯国|(春秋)}}
{{Otheruses|ヨーロッパの国|長崎県・熊本県の郷土料理|いぎりす}}
{{基礎情報 国
|略名  =イギリス
|日本語国名 = グレートブリテン及び北アイルランド連合王国
.
.
.

21. カテゴリ名を含む行を抽出

記事中でカテゴリ名を宣言している行を抽出せよ.

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]
pattern = r'^(.*\[\[Category:.*\]\].*)$'
result = '\n'.join(re.findall(pattern, wiki_uk, re.MULTILINE))
print(result)
[[Category:イギリス|*]]
[[Category:イギリス連邦加盟国]]
[[Category:英連邦王国|*]]
[[Category:G8加盟国]]
[[Category:欧州連合加盟国|]]
[[Category:海洋国家]]
[[Category:現存する君主国]]
[[Category:島国]]
[[Category:1801年に成立した国家・領域]]

22. カテゴリ名の抽出

記事のカテゴリ名を(行単位ではなく名前で)抽出せよ.

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]
pattern = r'^.*\[\[Category:(.*?)(?:\|.*)?\]\].*$'
result = '\n'.join(re.findall(pattern, wiki_uk, re.MULTILINE))
print(result)
イギリス
イギリス連邦加盟国
英連邦王国
G8加盟国
欧州連合加盟国
海洋国家
現存する君主国
島国
1801年に成立した国家・領域

23. セクション構造

記事中に含まれるセクション名とそのレベル(例えば”== セクション名 ==”なら 1)を表示せよ.

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]
pattern = r'^(\={2,})\s*(.+?)\s*(\={2,}).*$'
result = '\n'.join(i[1] + ':' + str(len(i[0]) - 1) for i in re.findall(pattern, wiki_uk, re.MULTILINE))
print(result)
国名:1
歴史:1
地理:1
.
.
.
脚注:1
関連項目:1
外部リンク:1

24. ファイル参照の抽出

記事から参照されているメディアファイルをすべて抜き出せ.

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]
pattern = r'\[\[ファイル:(.+?)\|'
result = '\n'.join(re.findall(pattern, wiki_uk))
print(result)
Royal Coat of Arms of the United Kingdom.svg
Descriptio Prime Tabulae Europae.jpg
Lenepveu, Jeanne d'Arc au siège d'Orléans.jpg
.
.
.
CHANDOS3.jpg
The Fabs.JPG
Wembley Stadium, illuminated.jpg

25. テンプレートの抽出

記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ.

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]

# extract template
pattern = r'^\{\{基礎情報.*?$(.*?)^\}\}'
template = re.findall(pattern, wiki_uk, re.MULTILINE + re.DOTALL)

# get field name and value
pattern = r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))'
result = re.findall(pattern, template[0], re.MULTILINE + re.DOTALL)

result = dict(result)
for k, v in result.items():
    print(k + ':' + v)
.
.
.
国旗画像:Flag of the United Kingdom.svg
国章画像:[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
国章リンク:[[イギリスの国章|国章]])
標語:{{lang|fr|[[Dieu et mon droit]]}}<br />[[フランス語]]:[[Dieu et mon droit|神と我が権利]])
国歌:[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />''神よ女王を護り賜え''<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}
地図画像:Europe-UK.svg
位置画像:United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
公用語:[[英語]]
.
.
.

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

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

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]

# extract template
pattern = r'^\{\{基礎情報.*?$(.*?)^\}\}'
template = re.findall(pattern, wiki_uk, re.MULTILINE + re.DOTALL)

# get field name and value
pattern = r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))'
result = re.findall(pattern, template[0], re.MULTILINE + re.DOTALL)

# remove emphasis
pattern = re.compile(r'\'{2,5}', re.MULTILINE + re.S)
result = {i[0]:pattern.sub('', i[1]) for i in result}

result = dict(result)
for k, v in result.items():
    print(k + ':' + v)
.
.
.
国旗画像:Flag of the United Kingdom.svg
国章画像:[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
国章リンク:[[イギリスの国章|国章]])
標語:{{lang|fr|[[Dieu et mon droit]]}}<br />[[フランス語]]:[[Dieu et mon droit|神と我が権利]])
国歌:[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />神よ女王を護り賜え<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}
地図画像:Europe-UK.svg
位置画像:United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
公用語:[[英語]]
.
.
.

27. 内部リンクの除去

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

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]

# extract template
pattern = r'^\{\{基礎情報.*?$(.*?)^\}\}'
template = re.findall(pattern, wiki_uk, re.MULTILINE + re.DOTALL)

# get field name and value
pattern = r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))'
result = re.findall(pattern, template[0], re.MULTILINE + re.DOTALL)

# remove emphasis
pattern = re.compile(r'\'{2,5}', re.MULTILINE + re.S)
result = {i[0]:pattern.sub('', i[1]) for i in result}

# remove inner link
pattern = r'\[\[(?:[^|]*?\|)??([^|]*?)\]\]'
result = {k: re.sub(pattern, r'\1', v) for k, v in result.items()}

result = dict(result)
for k, v in result.items():
    print(k + ':' + v)
.
.
.
国旗画像:Flag of the United Kingdom.svg
国章画像:[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
国章リンク:(国章)
標語:{{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}}
地図画像:Europe-UK.svg
位置画像:United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
公用語:英語
.
.
.

28. MediaWiki マークアップの除去

27 の処理に加えて,テンプレートの値から MediaWiki マークアップを可能な限り除去し,国の基本情報を整形せよ.

import pandas as pd
import re

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]

# extract template
pattern = r'^\{\{基礎情報.*?$(.*?)^\}\}'
template = re.findall(pattern, wiki_uk, re.MULTILINE + re.DOTALL)

# get field name and value
pattern = r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))'
result = re.findall(pattern, template[0], re.MULTILINE + re.DOTALL)

# remove emphasis
pattern = re.compile(r'\'{2,5}', re.MULTILINE + re.S)
result = {i[0]:pattern.sub('', i[1]) for i in result}

# remove inner link
pattern = r'\[\[(?:[^|]*?\|)??([^|]*?)\]\]'
result = {k: re.sub(pattern, r'\1', v) for k, v in result.items()}

# remove outer link
pattern = r'https?://[\w!?/\+\-_~=;\.,*&@#$%\(\)\'\[\]]+'
result = {k: re.sub(pattern, '', v) for k, v in result.items()}

# remove html tag
pattern = r'<.+?>'
result = {k: re.sub(pattern, '', v) for k, v in result.items()}

# remove template
pattern = r'\{\{(?:lang|仮リンク)(?:[^|]*?\|)*?([^|]*?)\}\}'
result = {k: re.sub(pattern, r'\1', v) for k, v in result.items()}


result = dict(result)
for k, v in result.items():
    print(k + ':' + v)
.
.
.
国旗画像:Flag of the United Kingdom.svg
国章画像:[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
国章リンク:(国章)
標語:Dieu et mon droit(フランス語:神と我が権利)
国歌:[[女王陛下万歳|God Save the Queen]]{{en icon}}神よ女王を護り賜え{{center|ファイル:United States Navy Band - God Save the Queen.ogg}}
地図画像:Europe-UK.svg
位置画像:United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
公用語:英語
.
.
.

29. 国旗画像の URL を取得する

テンプレートの内容を利用し,国旗画像の URL を取得せよ.(ヒント: MediaWiki API の imageinfo を呼び出して,ファイル参照を URL に変換すればよい)

import pandas as pd
import re
import requests

df = pd.read_json('jawiki-country.json.gz', lines=True)
wiki_uk = df.query('title == "イギリス"')['text'].values[0]

# extract template
pattern = r'^\{\{基礎情報.*?$(.*?)^\}\}'
template = re.findall(pattern, wiki_uk, re.MULTILINE + re.DOTALL)

# get field name and value
pattern = r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))'
result = re.findall(pattern, template[0], re.MULTILINE + re.DOTALL)

# remove emphasis
pattern = re.compile(r'\'{2,5}', re.MULTILINE + re.S)
result = {i[0]:pattern.sub('', i[1]) for i in result}

# remove inner link
pattern = r'\[\[(?:[^|]*?\|)??([^|]*?)\]\]'
result = {k: re.sub(pattern, r'\1', v) for k, v in result.items()}

# remove outer link
pattern = r'https?://[\w!?/\+\-_~=;\.,*&@#$%\(\)\'\[\]]+'
result = {k: re.sub(pattern, '', v) for k, v in result.items()}

# remove html tag
pattern = r'<.+?>'
result = {k: re.sub(pattern, '', v) for k, v in result.items()}

# remove template
pattern = r'\{\{(?:lang|仮リンク)(?:[^|]*?\|)*?([^|]*?)\}\}'
result = {k: re.sub(pattern, r'\1', v) for k, v in result.items()}

# get url
url_file = result['国旗画像'].replace(' ', '_')
url = 'https://commons.wikimedia.org/w/api.php?action=query&titles=File:' + url_file + '&prop=imageinfo&iiprop=url&format=json'
data = requests.get(url)
print(re.search(r'"url":"(.+?)"', data.text).group(1))
https://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_the_United_Kingdom_%283-5%29.svg

参考

https://nlp100.github.io/ja/about.html
https://nlp100.github.io/ja/ch03.html

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!