こんにちは。たかぱい(@takapy0210)です。
本エントリは言語処理100本ノック 2020の3章を解いてみたので、それの備忘です。
例によってコードはGithubに置いてあります。
第3章: 正規表現
Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある.
- 1行に1記事の情報がJSON形式で格納される
- 各行には記事名が”title”キーに,記事本文が”text”キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
- ファイル全体はgzipで圧縮される
以下の処理を行うプログラムを作成せよ.
20. JSONデータの読み込み
""" Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ. 問題21-29では,ここで抽出した記事本文に対して実行せよ. """ import pandas as pd df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] print(uk_wiki)
read_json
で圧縮されているデータも読み込めます。便利ですね。
21. カテゴリ名を含む行を抽出
""" 記事中でカテゴリ名を宣言している行を抽出せよ. """ import pandas as pd import re df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] pattern = re.compile(r'^(.*\[\[Category:.*\]\].*)$', re.MULTILINE) ans = '\n'.join(pattern.findall(uk_wiki)) print(ans)
この辺りの記事を参考に正規表現を使って抽出しました。
22. カテゴリ名の抽出
""" 記事のカテゴリ名を(行単位ではなく名前で)抽出せよ. """ import pandas as pd import re df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] pattern = re.compile(r'^.*\[\[Category:(.*?)(?:\|.*)?\]\].*$', re.MULTILINE) ans = '\n'.join(pattern.findall(uk_wiki)) print(ans)
23. セクション構造
""" 記事中に含まれるセクション名とそのレベル(例えば”== セクション名 ==”なら1)を表示せよ. """ import pandas as pd import re df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] pattern = re.compile(r'^(\={2,})\s*(.+?)\s*(\={2,}).*$', re.MULTILINE) ans = '\n'.join(i[1] + ':' + str(len(i[0])-1) for i in pattern.findall(uk_wiki)) print(ans)
==
も抽出対象とし、文字列の長さをセクション名と同時に表示させています。
24. ファイル参照の抽出
""" 記事から参照されているメディアファイルをすべて抜き出せ. """ import pandas as pd import re df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] pattern = re.compile(r'\[\[ファイル:(.+?)\|') ans = '\n'.join(pattern.findall(uk_wiki)) print(ans)
File
は無さそうだったのでファイル
だけにしています。
25. テンプレートの抽出
""" 記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ. """ import pandas as pd import re df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] # 基礎情報テンプレートの抽出 pattern = re.compile(r'^\{\{基礎情報.*?$(.*?)^\}\}', re.MULTILINE + re.S) base = pattern.findall(uk_wiki) # 抽出結果からのフィールド名と値の抽出 pattern = re.compile(r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))', re.MULTILINE + re.S) ans = pattern.findall(base[0]) ans = dict(ans) for k, v in ans.items(): print(k + ':' + v)
はじめにbase
変数に基礎情報テンプレートの値を設定し、その情報をさらに正規表現を用いてans
に格納します。
後半の正規表現では、=
の前後に空白があったり無かったりするので\s*
(空白文字0文字以上)を指定しています。
ans
にはtuple型で格納されるため、最後にdictへと変形して表示させています。
26. 強調マークアップの除去
""" 25の処理時に,テンプレートの値からMediaWikiの強調マークアップ(弱い強調,強調,強い強調のすべて) を除去してテキストに変換せよ(参考: マークアップ早見表). """ import pandas as pd import re df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] # 基礎情報テンプレートの抽出 pattern = re.compile(r'^\{\{基礎情報.*?$(.*?)^\}\}', re.MULTILINE + re.S) base = pattern.findall(uk_wiki) # 抽出結果からのフィールド名と値の抽出 pattern = re.compile(r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))', re.MULTILINE + re.S) ans = pattern.findall(base[0]) # 強調マークアップの除去 pattern = re.compile(r'\'{2,5}', re.MULTILINE + re.S) ans = {i[0]:pattern.sub('', i[1]) for i in ans} for k, v in ans.items(): print(k + ':' + v)
25のコードに強調マークアップの除去処理を追加
27. 内部リンクの除去
""" 26の処理に加えて,テンプレートの値からMediaWikiの内部リンクマークアップを除去し, テキストに変換せよ(参考: マークアップ早見表) """ import pandas as pd import re df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] # 基礎情報テンプレートの抽出 pattern = re.compile(r'^\{\{基礎情報.*?$(.*?)^\}\}', re.MULTILINE + re.S) base = pattern.findall(uk_wiki) # 抽出結果からのフィールド名と値の抽出 pattern = re.compile(r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))', re.MULTILINE + re.S) ans = pattern.findall(base[0]) # 強調マークアップの除去 pattern = re.compile(r'\'{2,5}', re.MULTILINE + re.S) ans = {i[0]:pattern.sub('', i[1]) for i in ans} # 内部リンクの除去 pattern = re.compile(r'\[\[(?:[^|]*?\|)??([^|]*?)\]\]', re.MULTILINE + re.S) ans = {k:pattern.sub('', v) for k, v in ans.items()} for k, v in ans.items(): print(k + ':' + v)
26のコードに内部リンクの除去処理を追加
28. MediaWikiマークアップの除去
""" 27の処理に加えて,テンプレートの値からMediaWikiマークアップを可能な限り除去し,国の基本情報を整形せよ. """ import pandas as pd import re def rm_markup(target): # 強調マークアップの除去 pattern = re.compile(r'\'{2,5}', re.MULTILINE) target = pattern.sub('', target) # 内部リンクの除去 pattern = re.compile(r'\[\[(?:[^|]*?\|)??([^|]*?)\]\]', re.MULTILINE) target = pattern.sub('', target) # Template:Langの除去 {{lang|言語タグ|文字列}} pattern = re.compile(r'\{\{lang(?:[^|]*?\|)*?([^|]*?)\}\}', re.MULTILINE) target = pattern.sub('', target) # 外部リンクの除去 [http://xxxx]/[http://xxx xxx] pattern = re.compile(r'\[http:\/\/(?:[^\s]*?\s)?([^]]*?)\]', re.MULTILINE) target = pattern.sub('', target) # <br>、<ref>の除去 pattern = re.compile(r'<\/?[br|ref][^>]*?>', re.MULTILINE) target = pattern.sub('', target) pattern = re.compile(r'({{Cite.*?}})$') target = pattern.sub('', target) # 改行コードの除去 target = target.replace('\n', '') return target df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] # 基礎情報テンプレートの抽出 pattern = re.compile(r'^\{\{基礎情報.*?$(.*?)^\}\}', re.MULTILINE + re.S) base = pattern.findall(uk_wiki) # 抽出結果からのフィールド名と値の抽出 pattern = re.compile(r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))', re.MULTILINE + re.S) ans = pattern.findall(base[0]) ans = {i[0]:rm_markup(i[1]) for i in ans} for k, v in ans.items(): print(k + ':' + v)
今までの処理をrm_markup
関数にまとめました。
頑張ればもう少し綺麗になると思いますが、今回はいったんこれで妥協しました汗
29. 国旗画像のURLを取得する
""" テンプレートの内容を利用し,国旗画像のURLを取得せよ. (ヒント: MediaWiki APIのimageinfoを呼び出して,ファイル参照をURLに変換すればよい) """ import pandas as pd import re import requests def rm_markup(target): # 強調マークアップの除去 pattern = re.compile(r'\'{2,5}', re.MULTILINE) target = pattern.sub('', target) # 内部リンクの除去 pattern = re.compile(r'\[\[(?:[^|]*?\|)??([^|]*?)\]\]', re.MULTILINE) target = pattern.sub('', target) # Template:Langの除去 {{lang|言語タグ|文字列}} pattern = re.compile(r'\{\{lang(?:[^|]*?\|)*?([^|]*?)\}\}', re.MULTILINE) target = pattern.sub('', target) # 外部リンクの除去 [http://xxxx]/[http://xxx xxx] pattern = re.compile(r'\[http:\/\/(?:[^\s]*?\s)?([^]]*?)\]', re.MULTILINE) target = pattern.sub('', target) # <br>、<ref>の除去 pattern = re.compile(r'<\/?[br|ref][^>]*?>', re.MULTILINE) target = pattern.sub('', target) pattern = re.compile(r'({{Cite.*?}})$') target = pattern.sub('', target) # 改行コードの除去 target = target.replace('\n', '') return target def get_url(text): url_file = text['国旗画像'].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) return re.search(r'"url":"(.+?)"', data.text).group(1) df = pd.read_json('jawiki-country.json.gz', lines=True) uk_wiki = df.query('title == "イギリス"')['text'].values[0] # 基礎情報テンプレートの抽出 pattern = re.compile(r'^\{\{基礎情報.*?$(.*?)^\}\}', re.MULTILINE + re.S) base = pattern.findall(uk_wiki) # 抽出結果からのフィールド名と値の抽出 pattern = re.compile(r'^\|(.+?)\s*=\s*(.+?)(?:(?=\n\|)| (?=\n$))', re.MULTILINE + re.S) ans = pattern.findall(base[0]) ans = {i[0]:rm_markup(i[1]) for i in ans} print(get_url(ans))