2018年03月25日

言語処理100本ノックでPython入門 #29 - HttpGetでJSONデータを取得

  
今日は、言語処理100本ノック 2015の第3章最後の問題 No.29です。

■ 問題

問題20で作成したファイルを入力として、以下の問題を解きます。
29. 国旗画像のURLを取得する
テンプレートの内容を利用し,国旗画像のURLを取得せよ.(ヒント: MediaWiki APIのimageinfoを呼び出して,ファイル参照をURLに変換すればよい)

■ Pythonのコード
import json
import re
import urllib.request
import urllib.parse

def readArticle(filename):
    with open(filename, 'r', encoding='utf8') as fin:
        return fin.read()

def getImageFileName(article):
    basicInfo = re.search(r'\{\{基礎情報(.+?)\}\}\n', article, re.DOTALL).group(1)
    pat = r'^\|国旗画像\s*=\s*(.+?)\n'
    reg = re.compile(pat, re.MULTILINE | re.DOTALL)
    m = reg.search(basicInfo)
    return m.group(1)

def getImage(finename):
    url = 'https://ja.wikipedia.org/w/api.php?' \
        + 'action=query' \
        + '&format=json' \
        + '&titles=File:' + urllib.parse.quote(finename) \
        + '&prop=imageinfo' \
        + '&iiprop=url'
    with urllib.request.urlopen(url) as res:
        data = json.loads(res.read().decode())
        return data['query']['pages']['-1']['imageinfo'][0]['url']


def main():
    # england-article.txtは、問題20(nlp20.py)で作成したファイル
    article = readArticle('england-article.txt')  
    fname = getImageFileName(article)
    print(getImage(fname))

if __name__ == '__main__':
    main()

■ 今回のトピック

ソースコードの1文を複数行に分ける

ソースコード上で、複数行にわたる文字列を連結するには、行末に \ が必要。
url = 'https://ja.wikipedia.org/w/api.php?' \
    + 'action=query' \
    + '&format=json' \
    + '&titles=File:' + urllib.parse.quote(finename) \
    + '&prop=imageinfo' \
    + '&iiprop=url'

最初は、この \ を書かなかったら、
E0001:unexpected indent

が表示されてしばらく意味わかりませんでした。

文字列連結にかかわらず、本来1行のものを複数行に分ける場合は、\ が必要ということらしいです。 そういえば、No13 で既に使ってました。すっかり忘れています。

URLエンコード
import urllib.parse

encoded = urllib.parse.quote(s)

文字列sをurlエンコードします。
quoteというメソッド名にものすごい違和感を感じるのですが、それは僕の英語力がないせいでしょうか?


Http Get
import urllib.request

with urllib.request.urlopen(url) as res:
    bytes = res.read()

指定したurlからHttp Getでデータを取得します。readメソッドで読み込んだデータはbyte配列です。


byte配列をstringにする


byte配列をstringにするには、decodeメソッドを呼び出します。

with urllib.request.urlopen(url) as res:
    data = json.loads(res.read().decode())

これで得た文字列を、json.loadsに渡し、jsonを辞書形式に変換します。


ちなみに、取得したjsonデータは以下の通りです。

{
    'query': {
        'normalized': [
            {'from': 'File:Flag of the United Kingdom.svg', 
             'to': 'ファイル:Flag of the United Kingdom.svg'
            }
        ], 
        'pages': {
            '-1': {
                'missing': '', 
                'known': '', 
                'title': 'ファイル:Flag of the United Kingdom.svg', 
                'ns': 6, 
                'imagerepository': 'shared', 
                'imageinfo': [
                    {
                        'descriptionshorturl': 'https://commons.wikimedia.org/w/index.php?curid=347935', 
                        'descriptionurl': 'https://commons.wikimedia.org/wiki/File:Flag_of_the_United_Kingdom.svg', 
                        'url': 'https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg'
                    }
                ]
            }
        }
    }, 
    'continue': {
        'continue': '||', 
        'iistart': '2007-09-03T09:51:34Z'
    }
}
このデータの中から、以下のようにして国旗画像のURLを取得します。階層構造の深いところにあるので面倒です。

js['query']['pages']['-1']['imageinfo'][0]['url']


■ 結果
https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg



これで、第3章が終わりました。
まだ3割。先が長いです。 そもそも僕の実力で最後まで解くことができるのでしょうか。 モチベーションが継続するかも問題だなー。