Python

Python の HTTP クライアントは urllib.request で十分

AWS Lambda が Python 3 に対応してからというもの Lambda Functions はだいたい Python 3 で書くようになったが、Lambda では外部モジュールに依存する関数を作ろうとすると一気に面倒臭さが増してしまう。そもそも Python は標準モジュールが充実していてよほど複雑なことをしなければ標準モジュールだけで済ませることができるのだから、極力外部モジュールに頼らない書き方ができるとより気軽に Lambda Function を書けるようになる。

例えば Python から HTTP API を利用する場合、何も考えずにすぐ requests などの便利な HTTP クライアントモジュールを入れてしまいがちだが、冷静になって考えると「その程度の用途なら urllib.request で十分では」と思うことは少なくない。

そこで今回は HTTP API を利用する場合の urllib.request の使い方についてまとめる。

GET リクエストを投げる

Request オブジェクトを作成し、urlopen に渡すことでリクエストが送信される。

import urllib.request

url = 'https://example.com/api/v1/resource'

req = urllib.request.Request(url)
with urllib.request.urlopen(req) as res:
    body = res.read()

urlopen で得られるレスポンスは http.client.HTTPResponse オブジェクト なので、レスポンスヘッダやステータスコードなどもこのオブジェクトから取得することができる。ただし HTTP ステータスコードが 4xx と 5xx の場合は urllib.error.HTTPError 例外が投げられるため、例外処理を書く必要がある。(後述)

QueryString でリクエストパラメータを送る

urllib.parse.urlencode を使って URL を組み立てる。

import urllib.request

url = 'https://example.com/api/v1/resource'
params = {
    'foo': 123,
}

req = urllib.request.Request('{}?{}'.format(url, urllib.parse.urlencode(params)))
with urllib.request.urlopen(req) as res:
    body = res.read()

POST リクエストを投げる

Request オブジェクト作成時に data パラメータを指定すると POST メソッドとしてリクエストが投げられる。

以下は Content-Type: application/json でデータを送信する例。

import json
import urllib.request

url = 'https://example.com/api/v1/resource'
data = {
    'foo': 123,
}
headers = {
    'Content-Type': 'application/json',
}

req = urllib.request.Request(url, json.dumps(data).encode(), headers)
with urllib.request.urlopen(req) as res:
    body = res.read()

PUT PATCH DELETE リクエストを投げる

Requestmethod パラメータを渡せば GET POST 以外のメソッドも利用できる。

req = urllib.request.Request(url, json.dumps(data).encode(), headers, method='PUT')

それ以外は「POST リクエストを投げる」と同様。

レスポンスの JSON を parse する

レスポンスオブジェクトをそのまま json.load に投げる。

import json
import urllib.request

url = 'https://example.com/api/v1/resource'

req = urllib.request.Request(url)
with urllib.request.urlopen(req) as res:
    body = json.load(res)

例外処理

urlopen は二種類の例外を投げる。

import urllib.request

url = 'https://example.com/api/v1/resource'

req = urllib.request.Request(url)
try:
    with urllib.request.urlopen(req) as res:
        body = res.read()
except urllib.error.HTTPError as err:
    print(err.code)
except urllib.error.URLError as err:
    print(err.reason)

注意点として HTTPErrorURLError のサブクラスであるため、両方を except したい場合は HTTPError を先に書く必要がある。

補足

HTTP API を叩くための最低限の使い方についてまとめたが、より詳細な使い方については公式ドキュメントを読むと良い。