概要
PythonでWebAPIによってデータをリクエストして取得する方法をまとめる。ここでは郵便番号配信サービスのzipcloudのAPIを利用する。
流れは以下の通り。
- 準備
urllib.request
ライブラリーのインポート- APIエンドポイントURLのセット
- パラメーターのセット
- パラメーターのエンコード
- リクエストとレスポンス
Request
オブジェクトの生成- リクエスト実行と
Response
オブジェクトの取得
- レスポンスオブジェクトのデコード
API仕様
zipcloudサイトの郵便番号検索APIの内容をまとめると以下の通り。
- リクエストURL:https://zipcloud.ibsnet.co.jp/api/search
- リクエストパラメーター
zipcode
~必須~郵便番号(ハイフンなし7桁の数字)callback
~オプション~JSONPとして出力する際のコールバック関数名limit
~オプション~同一の郵便番号で複数のデータが存在する場合に返される上限件数(デフォルト20)- urlパターンの例:https://zipcloud.ibsnet.co.jp/api/search?zipcode=1330051
コード
最初に実行コードの全体を示す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
################################################ # ライブラリーのインポート # JSON 操作用のライブラリー import json # HTTPリクエスト用のライブラリー import urllib.request ################################################ # 変数の設定 # APIエンドポイント # この例では郵便番号データ配信サービスのAPIエンドポイント url = 'https://zipcloud.ibsnet.co.jp/api/search' # APIに渡すパラメーター # この例ではハイフンなしの郵便番号文字列 params = { 'zipcode': '1330051' } # パラメーターをstringからbyte列にエンコード # この例ではZIPcodeは半角数字なのでstf-8のほかascii, shift_jisでもok encoded_params = urllib.parse.urlencode(params).encode('utf-8') ################################################ # リクエストとレスポンスの取得 # エンドポイントとパラメーターを指定してRequestオブジェクトを生成 request = urllib.request.Request(url, data=encoded_params) # リクエストの結果からResponeseオブジェクトを取得 response = urllib.request.urlopen(request) print('-------- Reponseオブジェクトの内容 --------') print(response) # Responseオブジェクトの文字列をUTF-8でデコード jsontext = response.read().decode('utf-8') print('-------- レスポンスをUTF-8でデコードした文字列 --------') print(jsontext) print('-------- レスポンスの文字列をJSONとして辞書に展開 --------') decoded_json = json.loads(jsontext) print(decoded_json) print('-------- レスポンスの結果部分のみを取り出す --------') result_json = decoded_json['results'][0] print(result_json) print('-------- 結果部分のJSONを整形 --------') formatted_json = json.dumps(result_json, indent=4) print(formatted_json) print('-------- JSONの整形表示にあたってエスケープを抑制') json_utf8 = json.dumps(result_json, ensure_ascii=False, indent=4) print(json_utf8) |
コード説明
準備
urllib.requestライブラリーのインポート
HTTPリクエストに必要なurllib.request
ライブラリーをインポートする。
1 2 |
# HTTPリクエスト用のライブラリー import urllib.request |
APIエンドポイントURLのセット
ここではzipcloudのAPIエンドポイントのURLを変数url
にセットする。
1 2 3 |
# APIエンドポイント # この例では郵便番号データ配信サービスのAPIエンドポイント url = 'https://zipcloud.ibsnet.co.jp/api/search' |
リクエストパラメーターのセット
リクエストパラメーターを辞書でセット。ここでは仕様に従ってzipcode
に'1130051'
を与えている。
1 2 3 4 5 |
# APIに渡すパラメーター # この例ではハイフンなしの郵便番号文字列 params = { 'zipcode': '1330051' } |
パラメーターのエンコード
リクエストに備えて、パラメーター文字列をバイト列にエンコード。ここでは一般的なutf-8を指定しているが、郵便番号は半角数字なのでascii
やshift_jis
でも同じ結果となる。その形式は
urllib.parse.urlencode(パラメーター辞書).encode('文字コード')
1 2 3 |
# パラメーターをstringからbyte列にエンコード # この例ではZIPcodeは半角数字なのでstf-8のほかascii, shift_jisでもok encoded_params = urllib.parse.urlencode(params).encode('utf-8') |
リクエストとレスポンス
Requestオブジェクトの生成
ベースのURLとエンコードされたパラメーターを与えてRequest
オブジェクトを生成する。
urllib.request.Request(URL文字列, data=エンコード後のパラメーター)
1 2 |
# エンドポイントとパラメーターを指定してRequestオブジェクトを生成 request = urllib.request.Request(url, data=encoded_params) |
リクエスト実行とResponseオブジェクト取得
リクエストはRequest
オブジェクトを引数としてurlopen
メソッドを実行。戻り値は結果のResponse
オブジェクト。
1 2 |
# リクエストの結果からResponseオブジェクトを取得 response = urllib.request.urlopen(request) |
Responseオブジェクトのデコード
取得したままのResponse
オブジェクトをそのまま表示しても、クラスの文字列表現となるだけ。
1 2 3 4 |
# Responseオブジェクト print(response) # <http.client.HTTPResponse object at 0x7fb6598afeb8> |
そこで、文字コードを指定してデコードしてやる。ここではutf-8
を指定している。
response.read().decode(文字コード)
結果は文字コードで指定されたエンコード方式でデコードされ、レスポンスの文字列が得られる。
zipcloudのレスポンスはJSON形式でインデント整形された文字列となるが、あくまで文字列であり、要素を指定した値の取出しはできない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Responseオブジェクトの文字列をUTF-8でデコード jsontext = response.read().decode('utf-8') print('-------- レスポンスをUTF-8でデコードした文字列 --------') print(jsontext) # { # "message": null, # "results": [ # { # "address1": "東京都", # "address2": "江戸川区", # "address3": "北小岩", # "kana1": "トウキョウト", # "kana2": "エドガワク", # "kana3": "キタコイワ", # "prefcode": "13", # "zipcode": "1330051" # } # ], # "status": 200 # } |
JSON文字列の取り扱い
準備
JSON形式の文字列を辞書として読み込んだり、整形表示をしてみる(PythonでのJSONの取り扱い)。
まず、JSONを扱うにはjson
ライブラリーが必要。
1 |
import json |
辞書への展開
JSON形式の文字列をPythonの辞書として取り込む。
json.loads(JSON形式の文字列)
1 2 3 4 5 6 |
decoded_json = json.loads(jsontext) print(decoded_json) # {'message': None, 'results': [{'address1': '東京都', 'address2': '江戸川区', # 'address3': '北小岩', 'kana1': 'トウキョウト', 'kana2': 'エドガワク', 'kana3': 'キタコイワ', # 'prefcode': '13', 'zipcode': '1330051'}], 'status': 200} |
レスポンス内容の取出し
これはzipcloudの仕様になるが、レスポンスのJSONデータのレスポンス部分は入れ子の内側('response'
キーに対応)にあるので、これを取り出す。
同じ郵便番号に複数の住所が存在する場合もあるが、ここでは最初の1つだけ取り出している。
1 2 3 4 5 |
result_json = decoded_json['results'][0] print(result_json) # {'address1': '東京都', 'address2': '江戸川区', 'address3': '北小岩', 'kana1': 'トウキョウト', # 'kana2': 'エドガワク', 'kana3': 'キタコイワ', 'prefcode': '13', 'zipcode': '1330051'} |
JSON形式での整形出力
辞書の内容を再度整形して文字列化する。
json.dumps(辞書, indent=桁数)
ただし、このままではマルチバイト文字が16進にエスケープされてしまう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
formatted_json = json.dumps(result_json, indent=4) print(formatted_json) # { # "address1": "\u6771\u4eac\u90fd", # "address2": "\u6c5f\u6238\u5ddd\u533a", # "address3": "\u5317\u5c0f\u5ca9", # "kana1": "\uff84\uff73\uff77\uff6e\uff73\uff84", # "kana2": "\uff74\uff84\uff9e\uff76\uff9e\uff9c\uff78", # "kana3": "\uff77\uff80\uff7a\uff72\uff9c", # "prefcode": "13", # "zipcode": "1330051" # } |
エスケープの抑制
json.dumps
でエスケープを抑制することで、もとの文字コードに基づいて処理される。エスケープを抑制するためには引数でensure_ascii=False
をセットする。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
json_utf8 = json.dumps(result_json, ensure_ascii=False, indent=4) print(json_utf8) # { # "address1": "東京都", # "address2": "江戸川区", # "address3": "北小岩", # "kana1": "トウキョウト", # "kana2": "エドガワク", # "kana3": "キタコイワ", # "prefcode": "13", # "zipcode": "1330051" # } |