Edited at

素人の言語処理100本ノック:22

More than 1 year has passed since last update.

言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。


第3章: 正規表現


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

・1行に1記事の情報がJSON形式で格納される

・各行には記事名が"title"キーに,記事本文が"text"キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される

・ファイル全体はgzipで圧縮される

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



22. カテゴリ名の抽出


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



出来上がったコード:


main.py

# coding: utf-8

import gzip
import json
import re
fname = 'jawiki-country.json.gz'

def extract_UK():
'''イギリスに関する記事本文を取得

戻り値:
イギリスの記事本文
'''

with gzip.open(fname, 'rt') as data_file:
for line in data_file:
data_json = json.loads(line)
if data_json['title'] == 'イギリス':
return data_json['text']

raise ValueError('イギリスの記事が見つからない')

# 正規表現のコンパイル
pattern = re.compile(r'''
^ # 行頭
.* # 任意の文字0文字以上
\[\[Category:
( # キャプチャ対象のグループ開始
.*? # 任意の文字0文字以上、非貪欲マッチ(貪欲にすると後半の'|'で始まる装飾を巻き込んでしまう)
) # グループ終了
(?: # キャプチャ対象外のグループ開始
\|.* # '|'に続く0文字以上
)? # グループ終了、0か1回の出現
\]\]
.* # 任意の文字0文字以上
$ # 行末
'''
, re.MULTILINE + re.VERBOSE)

# 抽出
result = pattern.findall(extract_UK())

# 結果表示
for line in result:
print(line)



実行結果:


端末

イギリス

英連邦王国
G8加盟国
欧州連合加盟国
海洋国家
君主国
島国
1801年に設立された州・地域


貪欲マッチ

今回苦労したのは、[[Category:イギリス|*]]|*の部分や、[[Category:島国|くれいとふりてん]]|くれいとふりてんをどうやって取り除くかでした。最初は前半のキャプチャしたい部分を(.*)という形の貪欲(どんよく)マッチにしていましたが、うまくいきません。

取り除きたい部分は(\|.*)で良さそうなのですが、この|で始まる部分は存在しないこともあるので、このブロックには0回か1回を示す?を付けて(\|.*)?としました。しかしこれだと、前半の(.*)の方に|で始まる部分も該当するため、前半が全部持って行ってしまいます。そこでこの前半の部分を非貪欲マッチ(.*?)にすることで、解決しました。


キャプチャ対象外のグループ

取り除きたい部分はキャプチャする必要がないので、(...)ではなく(?:...)としています。


もう1つ考えたコード

[[Category:...]]を抽出してから|で始まる部分を取り除くという考え方ではなく、初めから]]|が出てくるまでをキャプチャする、という形も考えました。


正規表現コンパイル部分その2(main2.py)

# 正規表現のコンパイル その2

pattern = re.compile(r'''
^ # 行頭
.* # 任意の文字0文字以上
\[\[Category:
(.*?) # キャプチャ対象、任意の文字0文字以上、非貪欲マッチ
(?:\]\]|\|) # キャプチャ対象外、']]'または'|'
.* # 任意の文字0文字以上
$ # 行末
'''
, re.MULTILINE + re.VERBOSE)

こちらでも同じ結果になります。

ただ、どちらのコードも何かイマイチ。もう少しスマートに書く方法がありそうな気はします。

 

23本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第3章で用いているデータのライセンスはクリエイティブ・コモンズ 表示-継承 3.0 非移植日本語訳)です。