言語処理100本ノック with Python【第3章】

No 日付 学んだこと
20 4/13 jsongzip
21 4/14 正規表現、re、raise
22 4/16 正規表現

www.cl.ecei.tohoku.ac.jp



第3章: 正規表現

Wikipediaのページのマークアップ記述に正規表現を適用することで,様々な情報・知識を取り出します.

20. JSONデータの読み込み

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

1行に1記事の情報がJSON形式で格納されるので、2章のように1行読み込んでifで判定させる。

解法

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

with gzip.open(fname, 'rt', encoding="utf-8_sig") as jsonfile:
    for line in jsonfile:
        l = json.loads(line)
        if l['title'] == 'イギリス':
            print(l['text'])
            break

以下のように内包表記を使って書こうと思ったけどエラーが出てしまって無理だった。。
44行目を参考にifではなくwhileを使用してみた。
もしPythonの内包表記に終了条件が指定できたら · GitHub

result = [json.loads(line) for line in jsonfile while json.loads(line) != 'イギリス']
json
# 基本的な使い方
import json
fname = open('sample.json')

print (json.load(fname))

・ファイルの入出力(ファイル ⇔ Pythonオブジェクト)

load() JSON形式のファイルを読み込んでPythonオブジェクトへ
読み込んだJSONファイルは辞書型で保存される
dump() PythonオブジェクトをJSON形式でファイルに書き込み
こっちは文字列型で保存される

・オブジェクトの変換(文字列 ⇔ Pythonオブジェクト)

loads() JSON文字列からPythonオブジェクトへ変換(デコード)
dumps() PythonオブジェクトからJSON文字列へ変換(エンコード

19.2. json — JSON エンコーダおよびデコーダ — Python 3.6.5 ドキュメント

gzip
gzip.open(filename, mode='rb', compresslevel=9, encoding=None, errors=None, newline=None)

'rt'はread + text
13.2. gzip — gzip ファイルのサポート — Python 3.6.5 ドキュメント

gzという拡張子は見慣れなかったけど圧縮形式の一つで「GNU zip」の略らしい。
gunzipコマンドで解凍可能。




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

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

素人の言語処理100本ノック:21 - Qiita
参考のQiita記事ではraiseを使った処理を書いているけどtry-exceptとの違いがわかってないのでメモる。

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

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

    raise ValueError('Not Found Article.')


# 正規表現のコンパイル
pattern = re.compile(r'^(.*\[\[Category:.*\]\].*)$', re.MULTILINE + re.VERBOSE)

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

for line in result:
    print(line)

# [[Category:イギリス|*]]
# [[Category:英連邦王国|*]]
# [[Category:G8加盟国]]
# [[Category:欧州連合加盟国]]
# [[Category:海洋国家]]
# [[Category:君主国]]
# [[Category:島国|くれいとふりてん]]
# [[Category:1801年に設立された州・地域]]
正規表現
メタ文字 説明 使用例
. 任意の一文字 a.c
^ 文字列の先頭 ^abc
$ 文字列の末尾 abc$
* 0回以上の繰り返し ab*
+ 1回以上の繰り返し ab+
? 0回または1回 ab?
.* 任意の文字0文字以上
.*? 任意の文字0文字以上、非貪欲マッチ 貪欲だと後半の"l"で始まる装飾を巻き込んでしまう

Pythonの正規表現の基本的な使い方 | UX MILK

re
raise

22. カテゴリ名の抽出

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

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

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

def get_UK_article():
    with gzip.open(fname, 'rt', encoding="utf-8_sig") as jsonfile:
        for line in jsonfile:
            line_json = json.loads(line)
            if line_json['title'] == 'イギリス':
                return line_json['text']

    raise ValueError('Not Found Article.')

pattern = re.compile(r'^.*\[\[Category:(.*?)(?:\|.*)?\]\].*$', re.MULTILINE + re.VERBOSE)

result = pattern.findall(get_UK_article())

for line in result:
    print(line)

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

23. セクション構造

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

素人の言語処理100本ノック:23 - Qiita

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

def get_UK_article():
    with gzip.open(fname, 'rt', encoding="utf-8_sig") as jsonfile:
        for line in jsonfile:
            line_json = json.loads(line)
            if line_json['title'] == 'イギリス':
                return line_json['text']

    raise ValueError('Not Found Article.')

pattern = re.compile(r'^(={2,})\s*(.+?)\s*\1.*$', re.MULTILINE + re.VERBOSE)

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

# 結果表示
for line in result:
    level = len(line[0]) - 1
    print('{indent}{sect}({level})'.format(
        indent='\t' * (level - 1), sect=line[1], level=level))
    
# 国名(1)
# 歴史(1)
# 地理(1)
# 	気候(2)
# 政治(1)
# 外交と軍事(1)
# 地方行政区分(1)
# 	主要都市(2)
# :
# :

24. ファイル参照の抽出

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

素人の言語処理100本ノック:24 - Qiita