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

はじめに

言語処理100本ノック2020

言語処理100本ノックは東北大学が公開している自然言語処理の問題集です。

とても良質なコンテンツで企業の研修や勉強会で使われています。

そんな言語処理100本ノックが2020年に改定されてました。昨今の状況を鑑みて、深層ニューラルネットワークに関する問題が追加されました。(その他にも細かい変更があります)

この記事では、言語処理100本ノック2020にPythonで取り組んでいきます。

他にも色々な解法があると思うので、一つの解答例としてご活用ください!

全100問の解説に戻る

20. JSONデータの読み込み

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

1
2
3
4
import pandas as pd
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
print (uk)

pd.read_jsonでjsonを読み込みます。gzipのままでも問題ありません。
タイトルはtitleカラムに、本文はtextカラムに保存されています。valuesnumpy.ndarrayに変換する処理です。

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

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

1
2
3
4
5
6
7
8
9
import pandas as pd
import re
pattern = re.compile('Category')
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
ls = uk[0].split('\n')
for line in ls:
if re.search(pattern, line):
print (line)

re.searchがマッチングに失敗した場合にNoneを返却することを利用しています。

22. カテゴリ名の抽出

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

1
2
3
4
5
6
7
8
9
import pandas as pd
import re
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
ls = uk[0].split('\n')
for line in ls:
if re.search(pattern, line):
line = line.replace('[[','').replace('Category:','').replace(']]','').replace('|*','').replace('|元','')
print (line)

正規表現で全てを記述せず、後処理としてreplaceで削除を行なっています。

23. セクション構造

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

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import re
pattern = re.compile('^=+.*=+$') # 1回以上の=で始まり、1回以上の=で終わる文字列
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
ls = uk[0].split('\n')
for line in ls:
if re.search(pattern, line):
level = line.count('=') // 2 - 1
print(line.replace('=',''), level )

同じパターンでなんどもマッチングをとる場合、re.compileで事前にコンパイルすると効率が良いです。

24. ファイル参照の抽出

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

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import re
pattern = re.compile('File|ファイル:(.+?)\|')
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
ls = uk[0].split('\n')
for line in ls:
r = re.findall(pattern, line)
if r:
print (r[0])

パターンマッチと同時に、丸括弧()で指定した部分文字列を抽出することができます。

25. テンプレートの抽出

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

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
import re
pattern = re.compile('\|(.+?)\s=\s*(.+)')
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
ls = uk[0].split('\n')
d = {}
for line in ls:
r = re.search(pattern, line)
if r:
d[r[1]]=r[2]
print (d)

|の後にkey=valueのようなフォーマットで記録されている。=の後にスペースが入っている場合とそうでない場合があるので注意。

1
2
\s 空白文字
* 直前のパターンの0回以上の繰り返し

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

import pandas as pd
import re
pattern = re.compile('\|(.+?)\s=\s*(.+)')
p_emp = re.compile('\'{2,}(.+?)\'{2,}')
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
ls = uk[0].split('\n')
d = {}
for line in ls:
r = re.search(pattern, line)
if r:
d[r[1]]=r[2]
r = re.sub(p_emp,'\\1', line)
print (r)
print (d)

27. 内部リンクの除去

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd
import re
pattern = re.compile('\|(.+?)\s=\s*(.+)')
p_emp = re.compile('\'{2,}(.+?)\'{2,}')
p_link = re.compile('\[\[(.+?)\]\]')
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
lines = uk[0]
lines = re.sub(p_emp,'\\1', lines)
lines = re.sub(p_link,'\\1', lines)
ls = lines.split('\n')
d = {}
for line in ls:
r = re.search(pattern, line)
if r:
d[r[1]]=r[2]
print (d)

\[\[(.+?)\]\][[]]で括られたテキストを抽出しています。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
import re
pattern = re.compile('\|(.+?)\s=\s*(.+)')
p_emp = re.compile('\'{2,}(.+?)\'{2,}')
p_link = re.compile('\[\[(.+?)\]\]')
p_refbr = re.compile('<[br|ref][^>]*?>.+?<\/[br|ref][^>]*?>')
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
lines = uk[0]
lines = re.sub(p_emp,'\\1', lines)
lines = re.sub(p_link,'\\1', lines)
lines = re.sub(p_refbr,'', lines)
ls = lines.split('\n')
d = {}
for line in ls:
r = re.search(pattern, line)
if r:
d[r[1]]=r[2]
print (d)

br|ref<br>タグや<ref>を除去しています。 他にもやるべきことはまだまだありそうです。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import pandas as pd
import re
import requests
pattern = re.compile('\|(.+?)\s=\s*(.+)')
wiki = pd.read_json('jawiki-country.json.gz', lines = True)
uk = wiki[wiki['title']=='イギリス'].text.values
ls = uk[0].split('\n')
d = {}
for line in ls:
r = re.search(pattern, line)
if r:
d[r[1]]=r[2]

S = requests.Session()
URL = "https://commons.wikimedia.org/w/api.php"
PARAMS = {
"action": "query",
"format": "json",
"titles": "File:" + d['国旗画像'],
"prop": "imageinfo",
"iiprop":"url"
}
R = S.get(url=URL, params=PARAMS)
DATA = R.json()
PAGES = DATA['query']['pages']
for k, v in PAGES.items():
print (v['imageinfo'][0]['url'])

公式のサンプルコードを参考にしました。

最後に

全100問の解説に戻る

記事情報

  • 投稿日:2020年5月9日
  • 最終更新日:2020年5月9日