schedule2019-02-07

22. カテゴリ名の抽出

はじめに

言語処理100本ノック 2015

Pythonを勉強するため、東京工業大学の岡崎教授が出題されている言語処理100本ノック 2015を解いていきます。

より深く理解するため、別解や利用したライブラリの解説もまとめていきます。

環境

  • Python3.6
  • OS : Linux Mint

OSがMacからLinux Mintに変わりました。

問題

3章からWikiを解析する問題になっています。

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

  • 1行に1記事の情報がJSON形式で格納される
  • 各行には記事名が"title"キーに,記事本文が"text"キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
  • ファイル全体はgzipで圧縮される
    以下の処理を行うプログラムを作成せよ.

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

Point

  • ファイルの読み込み、イギリスの記事抽出は20と同じ。
  • セクション行の判別と=の数を読み取る

レベルとセクションの例はこんな感じ。

1 : == セクションの見出し ==
2 : === サブセクションの見出し ===
3 : ==== サブサブセクションの見出し ====

解答:Python

正規表現で()は中身を取り出すことができます。 Pythonのre.match()関数では()で抽出した箇所の文字列を得ることができます。

セクション行は正規表現r'^=+.+=+$'で判別できます。

Source code

import gzip
import json
import re


def read_wiki(fname, tiltle):
    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']


def is_section(string):
    return re.match(r'^=+.+=+$', string)


def get_section_level(string):
    m = re.match(r'^(=+)(.+?)=+$', string)
    # m.group(1) => '==='
    return ' name : {0}\tlevel : {1} '.format(m.group(2), len(m.group(1)) - 1)


def main():
    fname = 'jawiki-country.json.gz'
    text = read_wiki(fname, 'イギリス').split('\n')
    # print(len(text))

    # カテゴリの行を表示する
    for line in text:
        if is_section(line):
            # print(line, end=' : ')
            print(get_section_level(line))


if __name__ == '__main__':
    main()

出力

セクション名とレベルをそれぞれ出しています。

$ python 23.\ セクション構造.py
 name : 国名    level : 1
 name : 歴史    level : 1
 name : 地理    level : 1
 name : 気候    level : 2
 name : 政治    level : 1
 name : 外交と軍事      level : 1
 name : 地方行政区分    level : 1
 name : 主要都市        level : 2
 name : 科学技術        level : 1
 name : 経済    level : 1
 name : 鉱業    level : 2
 name : 農業    level : 2
 name : 貿易    level : 2
 name : 通貨    level : 2
 name : 企業    level : 2
 name : 交通    level : 1
 name : 道路    level : 2
 name : 鉄道    level : 2
 name : 海運    level : 2
 name : 航空    level : 2
 name : 通信    level : 1
 name : 国民    level : 1
 name : 言語    level : 2
 name : 宗教    level : 2
 name :  婚姻   level : 2
 name : 教育    level : 2
 name : 文化    level : 1
 name : 食文化  level : 2
 name : 文学    level : 2
 name :  哲学   level : 2
 name : 音楽    level : 2
 name : イギリスのポピュラー音楽        level : 3
 name : 映画    level : 2
 name : コメディ        level : 2
 name : 国花    level : 2
 name : 世界遺産        level : 2
 name : 祝祭日  level : 2
 name : スポーツ        level : 1
 name : サッカー        level : 2
 name : 競馬    level : 2
 name : モータースポーツ        level : 2
 name : 脚注    level : 1
 name : 関連項目        level : 1
 name : 外部リンク      level : 1

解説

re.match(r'^=+.+=+$', string)でセクション行を判別。

セクション名とレベルの取得は関数get_section_level(string)で行っています。

def get_section_level(string):
    m = re.match(r'^(=+)(.+?)=+$', string)
    # m.group(1) => '==='
    return ' name : {0}\tlevel : {1} '.format(m.group(2), len(m.group(1)) - 1)

()の中身がm.group(n)で取れることを利用して=の長さなど取得しました。

次の記事

Python3で言語処理100本ノックまとめ

前の問題:22. カテゴリ名の抽出

次の問題:24. ファイル参照の抽出