<jupyter_start><jupyter_text>第3章: 正規表現Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある．* 1行に1記事の情報がJSON形式で格納される* 各行には記事名が”title”キーに，記事本文が”text”キーの辞書オブジェクトに格納され，そのオブジェクトがJSON形式で書き出される* ファイル全体はgzipで圧縮される以下の処理を行うプログラムを作成せよ．<jupyter_code>! wget https://nlp100.github.io/data/jawiki-country.json.gz
! gunzip jawiki-country.json.gz<jupyter_output>--2020-09-13 17:21:45--  https://nlp100.github.io/data/jawiki-country.json.gz
nlp100.github.io (nlp100.github.io) をDNSに問いあわせています... 172.16.31.10, 172.16.31.10, 192.168.3.11, ...
nlp100.github.io (nlp100.github.io)|172.16.31.10|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 5068362 (4.8M) [application/gzip]
`jawiki-country.json.gz' に保存中

jawiki-country.json 100%[===================>]   4.83M  3.82MB/s 時間 1.3s       

2020-09-13 17:21:47 (3.82 MB/s) - `jawiki-country.json.gz' へ保存完了 [5068362/5068362]<jupyter_text>20. JSONデータの読み込みWikipedia記事のJSONファイルを読み込み，「イギリス」に関する記事本文を表示せよ．問題21-29では，ここで抽出した記事本文に対して実行せよ．<jupyter_code>import json

JSON_FILE = 'jawiki-country.json'

def get_country_text(country):
    with open(JSON_FILE, encoding='utf-8') as f:
        # このJSONファイルは特殊で、１行ごとにJSONデータが保存されている。
        for line in f:
            data = json.loads(line)
            if data.get('title') == country:
                return data.get('text')

    return ''

desc_uk = get_country_text(u'イギリス')
print(desc_uk)<jupyter_output>{{redirect|UK}}
{{redirect|英国|春秋時代の諸侯国|英 (春秋)}}
{{Otheruses|ヨーロッパの国|長崎県・熊本県の郷土料理|いぎりす}}
{{基礎情報 国
|略名  =イギリス
|日本語国名 = グレートブリテン及び北アイルランド連合王国
|公式国名 = {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />
*{{lang|gd|An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath}}（[[スコットランド・ゲール語]]）
*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}（[[ウェールズ語]]）
*{{lang|ga|Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann}}（[[アイルランド語]]）
*{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh}}（[[コーンウォール語]]）
*{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}（[[スコットランド語]]）
**{{lang|sco|Claught Kängrick o Docht Brätain an Norlin Airlann}}、{{lang|sco|Unitet Kängdom o Great Brittain an Norlin Airlann}}（アルスター・スコットランド語）</ref>
|国旗画像 = Flag of the United Kingdom.svg
|国章画像 = [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
|国章リンク =（[[イギリスの国章|国章]]）
|標語 = {{lang|fr|[[Dieu et mon droit]]}}<br />（[[フランス語]]:[[Die[...]<jupyter_text>21. カテゴリ名を含む行を抽出記事中でカテゴリ名を宣言している行を抽出せよ．<jupyter_code>def get_categories(desc):
    return [line for line in desc.split('\n') if '[[Category:' in line]

desc_uk = get_country_text(u'イギリス')
for cat in get_categories(desc_uk):
    print(cat)<jupyter_output>[[Category:イギリス|*]]
[[Category:イギリス連邦加盟国]]
[[Category:英連邦王国|*]]
[[Category:G8加盟国]]
[[Category:欧州連合加盟国|元]]
[[Category:海洋国家]]
[[Category:現存する君主国]]
[[Category:島国]]
[[Category:1801年に成立した国家・領域]]<jupyter_text>22. カテゴリ名の抽出記事のカテゴリ名を（行単位ではなく名前で）抽出せよ．<jupyter_code>import re

def get_category_names(desc):
    res = []
    for line in get_categories(desc):
        cat_mo = re.search(r'\[{2}Category:(.*?)(\|.*)?]]', line)

        res.append(cat_mo.group(1))
        
    return res

desc_uk = get_country_text(u'イギリス')
for cat_name in get_category_names(desc_uk):
    print(cat_name)<jupyter_output>イギリス
イギリス連邦加盟国
英連邦王国
G8加盟国
欧州連合加盟国
海洋国家
現存する君主国
島国
1801年に成立した国家・領域<jupyter_text>23. セクション構造記事中に含まれるセクション名とそのレベル（例えば”== セクション名 ==”なら1）を表示せよ．<jupyter_code>import re

def print_section_struct(desc):
    for line in desc.split('\n'):
        cat_mo = re.search(r'^(=+)\s*(.+?)\s*=+$', line)
        if cat_mo is not None:
            print(cat_mo.group(2), len(cat_mo.group(1)) - 1)

desc_uk = get_country_text(u'イギリス')
print_section_struct(desc_uk)<jupyter_output>国名 1
歴史 1
地理 1
主要都市 2
気候 2
政治 1
元首 2
法 2
内政 2
地方行政区分 2
外交・軍事 2
経済 1
鉱業 2
農業 2
貿易 2
不動産 2
エネルギー政策 2
通貨 2
企業 2
通信 3
交通 1
道路 2
鉄道 2
海運 2
航空 2
科学技術 1
国民 1
言語 2
宗教 2
婚姻 2
移住 2
教育 2
医療 2
文化 1
食文化 2
文学 2
哲学 2
音楽 2
ポピュラー音楽 3
映画 2
コメディ 2
国花 2
世界遺産 2
祝祭日 2
スポーツ 2
サッカー 3
クリケット 3
競馬 3
モータースポーツ 3
野球 3
カーリング 3
自転車競技 3
脚注 1
関連項目 1
外部リンク 1<jupyter_text>24. ファイル参照の抽出記事から参照されているメディアファイルをすべて抜き出せ．<jupyter_code>def get_media_files(desc):
    res = []
    for line in desc.split('\n'):
        # １行に複数のメディアファイルがある場合があるので、１行ずつループ
        # Python 3.8からwhile条件文に代入が使えるようになった
        # 「ファイル：」以降はなるべく少ない範囲でマッチした範囲
        # 末尾は"|"か"]"でマッチさせる
        while (file_mo := re.search(r'ファイル:(.*?)([\|\]])', line)):            
            res.append(file_mo.group(1))
            line = line[file_mo.end():]
            
    return res

print(u'メディアファイル一覧')
desc_uk = get_country_text(u'イギリス')
for idx, filename in enumerate(get_media_files(desc_uk), start=1):
    print(idx, filename)<jupyter_output>メディアファイル一覧
1 Royal Coat of Arms of the United Kingdom.svg
2 United States Navy Band - God Save the Queen.ogg
3 Descriptio Prime Tabulae Europae.jpg
4 Lenepveu, <NAME>'Arc au siège d'Orléans.jpg
5 London.bankofengland.arp.jpg
6 Battle of Waterloo 1815.PNG
7 Uk topo en.jpg
8 BenNevis2005.jpg
9 Population density UK 2011 census.png
10 2019 Greenwich Peninsula & Canary Wharf.jpg
11 Birmingham Skyline from Edgbaston Cricket Ground crop.jpg
12 Leeds CBD at night.jpg
13 Glasgow and the Clyde from the air (geograph 4665720).jpg
14 Palace of Westminster, London - Feb 2007.jpg
15 Scotland Parliament Holyrood.jpg
16 Donald Trump and Theresa May (33998675310) (cropped).jpg
17 Soldiers Trooping the Colour, 16th June 2007.jpg
18 City of London skyline from London City Hall - Oct 2008.jpg
19 Oil platform in the North SeaPros.jpg
20 Eurostar at St Pancras Jan 2008.jpg
21 Heathrow Terminal 5C Iwelumo-1.jpg
22 Airbus A380-841 G-XLEB British Airways (10424102995).jpg
23 UKpop.svg
24 Anglospeak.svg
25 R[...]<jupyter_text>memo:`"[[ファイル:...]]"`で抽出しようとしたが、`[[..[[..]]..]]`のように入れ子になっているものもあり、対応するためにはコードが複雑になるため、ここではシンプルに実装した。 25. テンプレートの抽出記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し，辞書オブジェクトとして格納せよ．memo:* 「基礎情報」テンプレートは以下の範囲```{{基礎情報 国...}}```* フィールド名と値は以下のように記述されている```|国旗画像 = Flag of the United Kingdom.svg|国章画像 = [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]...```<jupyter_code>def get_basic_info_template(desc):
    """記事から基礎情報テンプレートを取り出す
    """
    res = []
    is_binfo = False
    for line in desc.split('\n'):
        if re.match(r'^\{{2}基礎情報.*$', line):
            is_binfo = True
            continue
        elif line == '}}':
            break

        if is_binfo:
            res.append(line)
            
    return res

def get_field_value(temp):
    """テンプレートからフィールドと値を取り出す
    """
    fv_mo = re.search(r'^\|(.*?)\s*?=\s*(.*)$', temp)
    if fv_mo is not None:
        return fv_mo.group(1, 2)
    else:
        return (None, None)

def create_dict_field_value(desc):
    """基礎情報テンプレートからフィールドと値の辞書を作る
    """
    field_value = {}
    for temp in get_basic_info_template(desc_uk):
        field, value = get_field_value(temp)
        if field is not None:
            field_value[field] = value

    return field_value
    
desc_uk = get_country_text(u'イギリス')
binfo_dic = create_dict_field_value(desc_uk)
for idx, field in enumerate(binfo_dic.keys()):
    print(f'{idx}: {field} = "{binfo_dic.get(field)}"')<jupyter_output>0: 略名 = "イギリス"
1: 日本語国名 = "グレートブリテン及び北アイルランド連合王国"
2: 公式国名 = "{{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />"
3: 国旗画像 = "Flag of the United Kingdom.svg"
4: 国章画像 = "[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]"
5: 国章リンク = "（[[イギリスの国章|国章]]）"
6: 標語 = "{{lang|fr|[[Dieu et mon droit]]}}<br />（[[フランス語]]:[[Dieu et mon droit|神と我が権利]]）"
7: 国歌 = "[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />''神よ女王を護り賜え''<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}"
8: 地図画像 = "Europe-UK.svg"
9: 位置画像 = "United Kingdom (+overseas territories) in the World (+Antarctica claims).svg"
10: 公用語 = "[[英語]]"
11: 首都 = "[[ロンドン]]（事実上）"
12: 最大都市 = "ロンドン"
13: 元首等肩書 = "[[イギリスの君主|女王]]"
14: 元首等氏名 = "[[エリザベス2世]]"
15: 首相等肩書 = "[[イギリスの首相|首相]]"
16: 首相等氏名 = "[[ボリス・ジョンソン]]"
17: 他元首等肩書1 = "[[貴族院 (イギリス)|貴族院議長]]"
18: 他元首等氏名1 = "[[:en:<NAME>, Baron Fowler|ノーマン・ファウラー]]"
19: 他元首等肩書2 = "[[庶民院 (イギリス)|庶民院議長]]"
20: 他元首等氏名2 = "{{仮リンク|リンゼイ・[...]<jupyter_text>26. 強調マークアップの除去25の処理時に，テンプレートの値からMediaWikiの強調マークアップ（弱い強調，強調，強い強調のすべて）を除去してテキストに変換せよ（参考: マークアップ早見表）．memo:48行目の「'''グレートブリテン及び北アイルランド連合王国'''」だけが対象。<jupyter_code>import re

def remove_emphasis(line):
    """強調マークアップの除去
    """
    return re.sub(r"'{2,5}", '', line)

desc_uk = get_country_text(u'イギリス')
binfo_dic = create_dict_field_value(desc_uk)
for idx, (field, val) in enumerate(binfo_dic.items(), start=1):
    val = remove_emphasis(binfo_dic.get(field))
    print(f'{idx}: {field} = "{val}"')<jupyter_output>1: 略名 = "イギリス"
2: 日本語国名 = "グレートブリテン及び北アイルランド連合王国"
3: 公式国名 = "{{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />"
4: 国旗画像 = "Flag of the United Kingdom.svg"
5: 国章画像 = "[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]"
6: 国章リンク = "（[[イギリスの国章|国章]]）"
7: 標語 = "{{lang|fr|[[Dieu et mon droit]]}}<br />（[[フランス語]]:[[Dieu et mon droit|神と我が権利]]）"
8: 国歌 = "[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />神よ女王を護り賜え<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}"
9: 地図画像 = "Europe-UK.svg"
10: 位置画像 = "United Kingdom (+overseas territories) in the World (+Antarctica claims).svg"
11: 公用語 = "[[英語]]"
12: 首都 = "[[ロンドン]]（事実上）"
13: 最大都市 = "ロンドン"
14: 元首等肩書 = "[[イギリスの君主|女王]]"
15: 元首等氏名 = "[[エリザベス2世]]"
16: 首相等肩書 = "[[イギリスの首相|首相]]"
17: 首相等氏名 = "[[ボリス・ジョンソン]]"
18: 他元首等肩書1 = "[[貴族院 (イギリス)|貴族院議長]]"
19: 他元首等氏名1 = "[[:en:<NAME>, <NAME>|ノーマン・ファウラー]]"
20: 他元首等肩書2 = "[[庶民院 (イギリス)|庶民院議長]]"
21: 他元首等氏名2 = "{{仮リンク|リンゼイ・ホイル[...]<jupyter_text>27. 内部リンクの除去26の処理に加えて，テンプレートの値からMediaWikiの内部リンクマークアップを除去し，テキストに変換せよ（参考: マークアップ早見表）．memo:内部リンクは以下の書式。```[[記事名]][[記事名|表示文字]][[記事名節名|表示文字]]```<jupyter_code>def split_internal_link(line):
    """内部リンクを分割
    """
    if re.match(r'\[{2}ファイル:', line):
        # ファイルリンクなら終了
        return None, None, None
    
    mo = re.search(r'\[{2}(.*?)(\#(.*?))?(\|(.*?))?\]{2}', line)
    if mo is not None:
        return mo.group(1, 3, 5)
    else:
        return None

def remove_emphasis_and_intrlink(val):
    """強調と内部リンクを除去
    """
    val = remove_emphasis(val)
    res = val
    while (intrl_mo := re.search(r'\[{2}.*?\]{2}', val)):
        #print('DEBUG:', intrl_mo.group(0))
        topic, subt, prstr = split_internal_link(intrl_mo.group(0))
        #print(topic)
        #print(subt)
        #print(prstr)
        replace_str = ''
        if prstr is not None:
            # 表示名があれば、表示名で置換
            replace_str = prstr
        elif topic is not None:
            # 表示名がなく、記事名があれば記事名で置換
            replace_str = topic
        
        if replace_str:
            res = res.replace(intrl_mo.group(0), replace_str)
            
        val = val[intrl_mo.end():]

    return res
    
desc_uk = get_country_text('uイギリス')
binfo_dic = create_dict_field_value(desc_uk)
for idx, (field, val) in enumerate(binfo_dic.items(), start=1):
    #print(f'{idx}: {field} = "{val}"')
    val = remove_emphasis_and_intrlink(binfo_dic.get(field))
    print(f'{idx}: {field} = "{val}"')<jupyter_output><empty_output><jupyter_text>28. MediaWikiマークアップの除去27の処理に加えて，テンプレートの値からMediaWikiマークアップを可能な限り除去し，国の基本情報を整形せよ．<jupyter_code>def remove_pattern(line, pattern, pos=0, repl=''):
    res = line
    while (mo := re.search(pattern, line)):
        if mo is not None:
            repl_str = ''
            if repl:
                repl_str = repl
            elif pos != 0 and mo.group(pos) is not None:
                repl_str = mo.group(pos)
            res = res.replace(mo.group(0), repl_str)
        
        line = line[mo.end():]
        
    return res

def remove_markup(line):
    """マークアップの削除
    """
    # 強調と内部リンクを削除
    res = remove_emphasis_and_intrlink(line)
    # langの削除
    res = remove_pattern(res, r'\{{2}lang\|..\|(.*?)\}{2}', 1)
    # ファイルの削除
    res = remove_pattern(res, r'\[{2}ファイル:(.*?)(\|(.*?))?(\|(.*))?\]{2}', 5)
    # 仮リンクの削除
    res = remove_pattern(res, r'\{{2}仮リンク\|(.*?)(\|(.*?))(\|(.*?))\}{2}', 1)
    # Cite webの削除
    res = remove_pattern(res, r'\{{2}Cite web.*?\}{2}')
    # centerの削除
    res = remove_pattern(res, r'\{{2}center.*?\}{2}')
    # <ref />の削除
    res = remove_pattern(res, r'<ref .*?/>')
    # {{0}}をスペースに置換
    res = remove_pattern(res, r'\{{2}0\}{2}', repl=' ')
    # <br />を改行に置換
    res = remove_pattern(res, r'<br\s*/>', repl='\n')
    # [http:... ]（外部リンク）を削除
    res = remove_pattern(res, r'\[http.*\]')
    # {en icon}を削除
    res = remove_pattern(res, r'\{en icon\}')
    # <ref .. />を削除
    res = remove_pattern(res, r'<ref\s+.*?/>')
    # <ref></ref>（空の)refを削除
    res = remove_pattern(res, r'<ref\s*.*?></ref>')
    # <ref>..<ref/>を削除
    res = remove_pattern(res, r'<ref\s*.*?>(.*?)</ref>', 1)
    # <references/>を削除
    res = remove_pattern(res, r'<references/>')

    return res

desc_uk = get_country_text(u'イギリス')
binfo_dic = create_dict_field_value(desc_uk)
for idx, (field, val) in enumerate(binfo_dic.items(), start=1):
    print(f'{idx}: {field} = "{val}"')
    val = remove_markup(binfo_dic.get(field))
    print(f'{idx}: {field} = "{val}"')<jupyter_output>1: 略名 = "イギリス"
1: 略名 = "イギリス"
2: 日本語国名 = "グレートブリテン及び北アイルランド連合王国"
2: 日本語国名 = "グレートブリテン及び北アイルランド連合王国"
3: 公式国名 = "{{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />"
3: 公式国名 = "United Kingdom of Great Britain and Northern Ireland<ref>英語以外での正式国名:
"
4: 国旗画像 = "Flag of the United Kingdom.svg"
4: 国旗画像 = "Flag of the United Kingdom.svg"
5: 国章画像 = "[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]"
5: 国章画像 = "イギリスの国章"
6: 国章リンク = "（[[イギリスの国章|国章]]）"
6: 国章リンク = "（国章）"
7: 標語 = "{{lang|fr|[[Dieu et mon droit]]}}<br />（[[フランス語]]:[[Dieu et mon droit|神と我が権利]]）"
7: 標語 = "Dieu et mon droit
（フランス語:神と我が権利）"
8: 国歌 = "[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />''神よ女王を護り賜え''<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}"
8: 国歌 = "God Save the Queen{}
神よ女王を護り賜え
"
9: 地図画像 = "Europe-UK.svg"
9: 地図画像 = "Europe-UK.svg"
10: 位置画像 = "United Kingdom (+overseas territories) in the World (+Antarctica claims).svg"
10: 位置画像 = "U[...]<jupyter_text>29. 国旗画像のURLを取得するテンプレートの内容を利用し，国旗画像のURLを取得せよ．（ヒント: MediaWiki APIのimageinfoを呼び出して，ファイル参照をURLに変換すればよい）memo:このページを参照。https://www.mediawiki.org/wiki/API:Imageinfo/ja<jupyter_code>import requests

MEDIAWIKI_URL = 'https://www.mediawiki.org/w/api.php'
MEDIAWIKI_PARAMS = {
    'action': 'query',
    'format': 'json',
    'prop'  : 'imageinfo',
    'iiprop': 'url',
}

def get_flag_picture_url(desc):
    # 国旗画像のテキストを取り出す
    binfo_dic = create_dict_field_value(desc_uk)
    flag_val = binfo_dic.get(u'国旗画像')
    fname = re.sub('^.*?:', '', re.sub(r'\|.*$', '', flag_val))
    
    # MediaWiki APIで国旗画像
    params = MEDIAWIKI_PARAMS
    params['titles'] = 'File:'+fname.replace(' ', '　')

    sess = requests.Session()
    res = sess.get(url=MEDIAWIKI_URL, params=params)
    res_json = res.json()

    # 画像URLを返す
    return res_json['query']['pages']['-1']['imageinfo'][0]['url']
    

desc_uk = get_country_text(u'イギリス')
flag_url = get_flag_picture_url(desc_uk)
print(flag_url)<jupyter_output>https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg
{'fraction_non_alphanumeric': 0.17378768020969856, 'fraction_numerical': 0.0327653997378768, 'mean_word_length': 4.087, 'pattern_counts': {'":': 0, '<': 75, '<?xml version=': 0, '>': 76, 'https://': 5, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '147711', 'license': 'MIT', 'n_tokens_mistral': 8259, 'n_tokens_neox': 8123, 'n_words': 1276, 'path': 'ch_03.ipynb', 'repo_name': 'kioto/nlp_100_2020', 'size': '15294'}