Python100本ノック(25/100)

## はじめに
Pythonを行き当たりばったりで触ってきたので
何処までPythonできているのかを見つめ直すために、[自然言語処理100本ノック](http://www.cl.ecei.tohoku.ac.jp/nlp100
)していきます。
python2ばっかり使っていましたので、慣れるためにpython3でやっていきます。

[Python100本ノック(25/100)](https://qiita.com/ikura1/items/e3666a99376bf9264247)の続きをやっていきます。

不慣れな章に入って苦戦しました。

一度終えてから、[素人の言語処理100本ノック](https://qiita.com/segavvy/items/fb50ba8097d59475f760)で答え合わせをしたのですが、ひどいありさまでした。
解答を見てからの修正したコードを追記します。

---
## 第3章: 正規表現
>Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある．

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

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

```python
import json
import gzip

path = 'jawiki-country.json.gz'
# wiki_jsons = [json.loads(line) for line in gzip.open(path, 'r').readlines()]
# filter(lambda x: x['title'] == 'イギリス', wiki_jsons)
for line in gzip.open(path, 'rt').readlines():
    page = json.loads(line)
    if page['title'] == 'イギリス':
        break
with open('england.json', 'w') as f:
    f.write(page['text'])
# print(page['text'])
```

途中でjson読み込みがうまくできなくて、困って検索すると100本ノックの解答を見てしまいました。
gzipというライブラリがあることがわかったので、pythonの中で完結させることにしました。
`readlines`で、一行ごとに`json.loads`で読み込みました。
そして、titleがイギリスの行を抽出しました。

### **21. カテゴリ名を含む行を抽出**
>記事中でカテゴリ名を宣言している行を抽出せよ．

```python
import gzip
import json
import re

path = 'jawiki-country.json.gz'
wiki_jsons = [json.loads(line) for line in gzip.open(path, 'r').readlines()]
text = list(filter(lambda x: x['title'] == 'イギリス', wiki_jsons))[0]['text']

# [line for line in text.split('\n') if 'Category:' in line]
# なにかあるはず……うまいことできるはず……
print([line for line in text.split('\n') if re.match(r'\[\[Category:.+\]\]', line) is not None])
```

    ['[[Category:イギリス|*]]', '[[Category:英連邦王国|*]]', '[[Category:G8加盟国]]', '[[Category:欧州連合加盟国]]', '[[Category:海洋国家]]', '[[Category:君主国]]', '[[Category:島国|くれいとふりてん]]', '[[Category:1801年に設立された州・地域]]']

jsonで戸惑いすぎて、3章が正規表現のことを完全に忘れていて苦戦しました。
行抽出なら、まだ`if in`で抽出できるので、さらに混乱してました。
なんとか正規表現のことを思い出し, [pythonの正規表現メモ](https://qiita.com/kaeruair/items/747518c116c85a88ee21)を参考にして解決しました。

```python
print('\n'.join(re.findall(r'(^.*\[\[Category:.+\]\].*$)', text, re.MULTILINE)))
```

`re.MULTILINE`を指定することで、マルチラインへ対応
`findall`で結果のグループを配列で返すように変更

### **22. カテゴリ名の抽出**
>記事のカテゴリ名を（行単位ではなく名前で）抽出せよ．

```python
import gzip
import json
import re
path = 'jawiki-country.json.gz'
wiki_jsons = [json.loads(line) for line in gzip.open(path, 'r').readlines()]
text = list(filter(lambda x: x['title'] == 'イギリス', wiki_jsons))[0]['text']
reg = re.compile(r'\[\[Category:(?P<value>.+)\]\]')
print([reg.match(line).group('value') for line in text.split('\n') if reg.match(line) is not None])

# [line.strip('[[]]').split(':')[1] for line in text.split('\n') if 'Category:' in line]
# わからん……なんもわからん……
# パースする方法があるんちゃうんか？
# それともここからパースするんか…？

```

    ['イギリス|*', '英連邦王国|*', 'G8加盟国', '欧州連合加盟国', '海洋国家', '君主国', '島国|くれいとふりてん', '1801年に設立された州・地域']

ここが、正規表現を使わないと簡単に解決できなくて悩んだ所です。
また、`re.group`で特定箇所が抽出できること・名前付きグループができることを初めて知りました。
すごい!っと思いつつ、使いました。

```python
reg = re.compile(r'\[\[Category:(?P<value>.+?)(?:\|.*)?\]\]')
print('\n'.join(reg.findall(text)))
```
`findall`を使うことで、ループを削除
|以降についていた文言を正規表現を修正して削除

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

```python
import gzip
import json
import re
path = 'jawiki-country.json.gz'
wiki_jsons = [json.loads(line) for line in gzip.open(path, 'r').readlines()]
text = list(filter(lambda x: x['title'] == 'イギリス', wiki_jsons))[0]['text']
reg = re.compile(r'(?P<level>=+)\s?(?P<name>.+?)\s?=+')
# print([len(reg.match(line).group('level')) - 1, reg.match(line).group('name') for line in text.split('\n') if reg.match(line) is not None])
for line in text.split('\n'):
    result = reg.match(line)
    if result is None:
        continue
    print(len(result.group('level')) - 1, result.group('name'))
```

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

正規表現を変更して、セクション名の箇所を抽出しました。
そして、=の数でセクションレベルを判断しました。
セクション名のスペースの有無での違いがわからない。

内包表記を使って、1行で書けないことはないです。
ですが、`re.match`を2回しなければいけないことが好きになれず、for文で書いています

```py
reg = re.compile(r'^(?P<level>={2,})\s*(?P<name>.+?)\s*={2,}', re.MULTILINE)
print('\n'.join(['{0}: {1}'.format(n, len(l) - 1) for l, n in reg.findall(text)]))
```
正規表現のバグを修正
・複数の空白や行頭にない場合に取得するバグ
・=が1個しかない場合に発見するバグ


### 24. **ファイル参照の抽出**
>記事から参照されているメディアファイルをすべて抜き出せ．

```python
import gzip
import json
import re
path = 'jawiki-country.json.gz'
wiki_jsons = [json.loads(line) for line in gzip.open(path, 'r').readlines()]
text = list(filter(lambda x: x['title'] == 'イギリス', wiki_jsons))[0]['text']
reg = re.compile(r'\[\[File:(?P<fname>.+?)\|')
# print([reg.match(line).group('fname') for line in text.split('\n') if reg.match(line) is not None])
for line in text.split('\n'):
    result = reg.match(line)
    if result is None:
        continue
    print(result.group('fname'))
```

    Battle of Waterloo 1815.PNG
    The British Empire.png
    Uk topo en.jpg
    BenNevis2005.jpg
    Elizabeth II greets NASA GSFC employees, May 8, 2007 edit.jpg
    Palace of Westminster, London - Feb 2007.jpg
    David Cameron and Barack Obama at the G20 Summit in Toronto.jpg
    Soldiers Trooping the Colour, 16th June 2007.jpg
    Scotland Parliament Holyrood.jpg
    London.bankofengland.arp.jpg
    City of London skyline from London City Hall - Oct 2008.jpg
    Oil platform in the North SeaPros.jpg
    Eurostar at St Pancras Jan 2008.jpg
    Heathrow T5.jpg
    Anglospeak.svg

正規表現を使い、`File:`がメディアファイルと思い、抽出しました。

```python
reg = re.compile(r'(?:ファイル|File):(?P<fname>.+?)\|')
print('\n'.join(reg.findall(text)))
```

`ファイル:`と`gallery`タグに存在するメディアファイルを取得できるように正規表現を修正

---

スーパー銭湯はいい。
でっかい風呂はいい。