言語処理100本ノック俺の解答 第3章

[言語処理100本ノック第3章: UNIXコマンド](https://nlp100.github.io/ja/ch03.html)の俺の解答。[その他の章はこちら](https://qiita.com/sangoro/items/9bb6b5c39e756818cce2)。

# 20. JSONデータの読み込み
Windows上でやっているので文字コードまわりでちょっと苦労した。
```Python
import argparse
import gzip
import json

parser = argparse.ArgumentParser()
parser.add_argument('-i', help="input gzipped JSON filename")
parser.add_argument('-o', help="outpuf filename")
args = parser.parse_args()

f_o = open(args.o, "w", encoding="utf_8", newline='\n')
f_i = gzip.open(args.i, 'rt', encoding="utf_8")
for l in f_i:
    tree = json.loads(l)
    if tree['title'] == 'イギリス':
        f_o.write(tree['text'])
```

# 21. カテゴリ名を含む行を抽出
20で作ったファイルを標準入力から受け取る前提。つまり、この21の解答を21.pyとすると、
```
$ python 21.py -o 21の出力 < 20の出力
```
とする、ということです。ふたたび、Windows上でUTF-8文字列を標準入力から受け取るのに少し面倒が。
```Python
import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('-o', help="outpuf filename")
args = parser.parse_args()

sys.stdin.reconfigure(encoding='utf-8')
f_o = open(args.o, "w", encoding="utf_8", newline='\n')
for l in sys.stdin:
    if l.startswith('[[Category:'):
        f_o.write(l)
```

# 22. カテゴリ名の抽出
```Python
import argparse
import sys
import re

parser = argparse.ArgumentParser()
parser.add_argument('-o', help="outpuf filename")
args = parser.parse_args()

regex = re.compile(r'^\[\[Category:([^|\]]+)')

sys.stdin.reconfigure(encoding='utf-8')
f_o = open(args.o, "w", encoding="utf_8", newline='\n')
for l in sys.stdin:
    matched = regex.match(l)
    if matched:
        f_o.write(matched.group(1)+'\n')
```

# 23. セクション構造
```Python
import argparse
import sys
import re

parser = argparse.ArgumentParser()
parser.add_argument('-o', help="outpuf filename")
args = parser.parse_args()

regex = re.compile(r'^(=+)([^=]+)')

sys.stdin.reconfigure(encoding='utf-8')
f_o = open(args.o, "w", encoding="utf_8", newline='\n')
for l in sys.stdin:
    matched = regex.match(l)
    if matched:
        level = len(matched.group(1)) - 1
        title = matched.group(2).strip()
        f_o.write(title+'\t'+str(level)+'\n')
```

# 24. ファイル参照の抽出
問題の定義が曖昧な気がする。どこまでがメディアファイル？PDFは含まれる？HTMLは？まぁ、どこまで含むかは問題の本質ではないので、適当に。
そして、Wikipediaの書式って、ファイル参照にいろんなやり方があって、正確かつ簡潔に参照を取り出すのはできなさそう。
ということは、どんな参照の仕方があるのか調べながら正規表現を作っていく過程が大事ということか。では、調べていきます。
```
$ grep '\.[a-zA-Z]' 20の出力 |less
```
として、例をピックアップすると、
```
|国旗画像 = Flag of the United Kingdom.svg
|国章画像 = [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリ
スの国章]]
|位置画像 = United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
[[ファイル:Descriptio Prime Tabulae Europae.jpg|thumb|left|[[クラウディオス・プ
トレマイオス|プトレマイオス]]の『[[地理学 (プトレマイオス)|地理学]]』に基づく地
図、アルビオンと[[:en:Hibernia|ヒベルニア]]（現在のアイルランド）の文字が見える]]
[[ファイル:Population density UK 2011 census.png|thumb|right|人口分布（2011年）]]
PalaceOfWestminsterAtNight.jpg|ウェストミンスター宮殿
```
といったところか。
ファイル名中に括弧が出てきて、実際にはそれがURLエンコードされて使われているとか、かなり厄介だ…。あんまりキリキリと正確性を求めても空しいので、めっちゃアドホックに適当に…
正規表現だけ書いておくと、
```Python
r'.*(?:^|\||=|:)([a-zA-Z ()+]+\.(?i:jpg|jpeg|svg|ogg))'
```
ああ、不本意だ…

# 25. テンプレートの抽出
```Python
import sys
import re

regex = re.compile(r'\|([^=]+)=(.*)$')

sys.stdin.reconfigure(encoding='utf-8')
for l in sys.stdin:
    if l.startswith("{{基礎情報 "):
        break

dic = {}
current = None
for l in sys.stdin:
    l = l.strip()
    matched = regex.match(l)
    if matched:
        current = matched.group(1)
        dic[current] = matched.group(2)
    elif l.startswith("*"):
        dic[current] += l.lstrip("*")
    elif l == "}}":
        break

print(dic)
```

# 26. 強調マークアップの除去
後方参照を使って。抜粋だけ書きます。
```Python
regex_emphasys = re.compile(r'(\'{2,3}|\'{5})([^\']*)\1')
def remove_emphasys(s):
    return regex_emphasys.sub(r'\2', s)
```

# 27. 内部リンクの除去
略。次にまとめて。

# 28. MediaWikiマークアップの除去
ひたすら正規表現を書いていく、まさに100本ノックですねぇ。
```Python
import sys
import re

regex = re.compile(r'\|([^=]+)=(.*)$')

sys.stdin.reconfigure(encoding='utf-8')
for l in sys.stdin:
    if l.startswith("{{基礎情報 "):
        break

dic = {}
current = None
for l in sys.stdin:
    l = l.strip()
    matched = regex.match(l)
    if matched:
        current = matched.group(1).strip()
        dic[current] = matched.group(2)
    elif l.startswith("*"):
        dic[current] += l.lstrip("*")
    elif l == "}}":
        break

regex_emphasys = re.compile(r'(\'{1,3})([^\']*)\1')
regex_ref = re.compile(r'<ref(?:[^>]+)?>.*</ref>')
regex_emptytag = re.compile(r'<\w+(?:[^/>]+)?/>')
regex_lang = re.compile(r'\{\{lang\|\w+\|([^\}]*)\}\}')
regex_icon = re.compile(r'\{\{\w+ icon\}\}')
regex_center = re.compile(r'\{\{center\|([^\}]+)\}\}')
regex_tmplink = re.compile(r'\{\{仮リンク\|(?:[^\}]+)\|([^\}]+)\}\}')
regex_digit = re.compile(r'\{\{\d\}\}')
regex_file = re.compile(r'\[\[ファイル:([^|\]]+)([^\]]+)?\]\]')
regex_link = re.compile(r'\[\[(?:[^\]]+\|)?([^\[\|\]]+)\]\]')
for key in dic:
    dic[key] = regex_emphasys.sub(r'\2', dic[key])
    dic[key] = regex_ref.sub(r' ', dic[key])
    dic[key] = regex_emptytag.sub(r' ', dic[key])
    dic[key] = regex_lang.sub(r'\1', dic[key])
    dic[key] = regex_icon.sub(r' ', dic[key])
    dic[key] = regex_center.sub(r'\1', dic[key])
    dic[key] = regex_tmplink.sub(r'\1', dic[key])
    dic[key] = regex_digit.sub(r'', dic[key])
    dic[key] = regex_file.sub(r'\1', dic[key])
    dic[key] = regex_link.sub(r'\1', dic[key])
    dic[key] = dic[key].strip()

print(dic)
```

# 29. 国旗画像のURLを取得する
言語処理っぽいところではないところで苦労した。
APIの接続先がhttps://www.mediawiki.org/w/api.php とか https://ja.wikipedia.org/w/api.php とかだと、pageidが-1でmissingという属性が入ったものが返ってくる。つまり目当てのものが見つからないということらしい。
で、[API:Imageinfoの説明](https://www.mediawiki.org/wiki/API:Imageinfo/ja)をよくよく眺めてみると、最後のほうのAdditional notesのところにFiles uploaded to the Wikimedia Commons will appear to be missing when accessed from other MediaWiki wikisなんて書いてあるので、もしかしてと思って https://commons.wikimedia.org/w/api.php に繋いでみたら、ビンゴ！
```Python
import requests

url = "https://commons.wikimedia.org/w/api.php"
params = {
    "action": "query",
    "prop": "imageinfo",
    "titles": "File:Flag of the United Kingdom.svg",
    "format": "json",
    "iiprop": "url"
}

json = requests.Session().get(url=url, params=params).json()
pages = json['query']['pages']
page = pages[next(iter(pages))] 
print(page['imageinfo'][0]['url'])
```
本当はファイル名を28で作った辞書型オブジェクトから取得すべきところですが、そんなことどうでもいいくらい苦労したので、そこはサボってます。