kNakajima's Blog

技術系のアウトプットブログです。

「言語処理100本ノック」 第3章をPythonで解く

言語処理100本ノックの3章を解きました。
3章は、正規表現を用いてwikipediaの文書を処理する問題です。
解答例としてどうぞ。質問,ご指摘などがありましたら、コメントしてください。

言語処理100本ノック第3章

20. JSONデータの読み込み

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

import json

def chose_uk():
    with open('jawiki-country.json', 'r') as json_file:
        for line in json_file:
            line = json.loads(line)
            if line['title'] == 'イギリス':
                uk_file = line
                break
                
    return uk_file
    
    
if __name__ == '__main__':     
    print(chose_uk())

jsonファイルを読み込むために標準ライブラリのjsonを利用します。

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

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

import re
import json

uk_file = chose_uk()

lines = uk_file['text'].split('\n')

pattern = r"\[\[Category:.*\]\]"

for line in lines:
    if re.match(pattern, line):
        print(line)

[[Category〜]]となっている部分を正規表現で抜き出します。

22. カテゴリ名の抽出

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

import re
import json

uk_file = chose_uk()

lines = uk_file['text'].split('\n')

pattern = r"\[\[Category:.*\]\]"

for line in lines:
    if re.match(pattern, line):
        line = re.sub(r"\[\[Category:", " ", line)
        line = re.sub(r"(\|.*)*(\|\*)*(\]\])", " ", line)
        print(line)

21で行った処理に加えて、[[ ]]などの余分な部分を正規表現で指定して削除します。

23. セクション構造

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

import re
import json

uk_file = chose_uk()

lines = uk_file['text'].split('\n')

pattern = r"=.+="

for line in lines:
    if re.match(pattern, line):
        result = line.count('=') // 2 - 1
        line = re.sub(r"=", "", line)
        print(line, 'レベル: {}'.format(result))

=で囲まれている部分を見つけて、=の数を数えてレベルを決めます。

24. ファイル参照の抽出

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

import re
import json

uk_file = chose_uk()

lines = uk_file['text'].split('\n')

pattern = r".*(ファイル|File):"

for line in lines:
    if re.match(pattern, line):
        line = re.sub(r".*(ファイル|File):", "", line)
        line = re.sub(r"\|.*", "", line)
        print(line)

25. テンプレートの抽出

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.

import re
import json

def make_uk_dict():
    uk_file = chose_uk()

    lines = uk_file['text'].split('\n')

    pattern = r"\|.*\s=\s.*"

    uk_dict = {}
    for line in lines:
        if re.match(pattern, line):      
            key = re.sub(r"(=.*|\|)", "", line)
            value = re.sub(r".*=", "", line)

            uk_dict[key] = value
            
    return uk_dict


if __name__ == '__main__':     
    uk_dict = make_uk_dict()
    for key in uk_dict:
        print(key, uk_dict[key]) 

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

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

import re
import json

def delete_markup(uk_dict):
    pattern = r".*'+.*'+"
    
    for key in uk_dict:
        if re.match(pattern, uk_dict[key]): 
            uk_dict[key] = re.sub(r"'+", "", line)
            
    return uk_dict

if __name__ == '__main__':
    uk_dict = make_uk_dict()
    print(delete_markup(uk_dict))

27. 内部リンクの除去

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

import re
import json

def delete_link(uk_dict):
    pattern = r".*\[\[.*\]\].*"

    for key in uk_dict:
        if re.match(pattern, uk_dict[key]): 
            if re.match(r"^(?!.*:).+$", uk_dict[key]):
                uk_dict[key] = re.sub(r".*\[\[.*\|", "", uk_dict[key])
                uk_dict[key] = re.sub(r"\[\[", "", uk_dict[key])
                uk_dict[key] = re.sub(r"\]\]\)|\]\]", "", uk_dict[key])
                
    return uk_dict
                
                
if __name__ == '__main__':
    uk_dict = make_uk_dict()
    uk_dict = delete_markup(uk_dict)
    uk_dict = delete_link(uk_dict)
    print(uk_dict)

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

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

import re
import json

def delete_media_markup(uk_dict):
    pattern = r".*{{.*\|.*\|.*}}.*"

    for key in uk_dict:
        if re.match(pattern, uk_dict[key]): 
            if re.match(pattern, uk_dict[key]):
                uk_dict[key] = re.sub(r"{{.*\|.*\|", "", uk_dict[key])
                uk_dict[key] = re.sub(r"}}", "", uk_dict[key])
                
    return uk_dict
            

if __name__ == '__main__':
    uk_dict = make_uk_dict()
    uk_dict = delete_markup(uk_dict)
    uk_dict = delete_link(uk_dict)
    uk_dict = delete_media_markup(uk_dict)
    print(uk_dict)
            

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

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

import re
import json
import requests

if __name__ == '__main__':
    uk_dict = make_uk_dict()
    uk_dict = delete_markup(uk_dict)
    uk_dict = delete_link(uk_dict)
    uk_dict = delete_media_markup(uk_dict)

    params = {'action':'query', 
              'titles':'File:' + uk_dict["国旗画像 "], 
              'format':'json', 
              'prop':'imageinfo', 'iiprop':'url', 
              'User-Agent':'knakajima'
             }
    url = 'https://www.mediawiki.org/w/api.php'
    result = requests.get(url, params).json()

    print(result['query']['pages']['-1'])