AWS Lambda が Python 3 に対応してからというもの Lambda Functions はだいたい Python 3 で書くようになったが、Lambda では外部モジュールに依存する関数を作ろうとすると一気に面倒臭さが増してしまう。1 そもそも 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]
(https://docs.python.jp/3/library/urllib.error.html#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
リクエストを投げる
Request
に method
パラメータを渡せば 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
は二種類の例外を投げる。
- urllib.error.URLError : HTTP 通信に失敗したとき
- urllib.error.HTTPError : HTTP ステータスコードが 4xx または 5xx だったとき
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)
注意点として HTTPError
は URLError
のサブクラスであるため、両方を except
したい場合は HTTPError
を先に書く必要がある。
セッション (Cookie) を扱う
セッション (Cookie) を扱いたい場合2はかなりやり方が異なる。
import json
import urllib.request
# Cookie を扱える OpenerDirector オブジェクトを作る
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor())
# ログインするための Request オブジェクトを組み立てる
url = 'https://example.com/login'
data = {
'username': 'foo',
'password': 'deadbeef',
}
headers = {
'Content-Type': 'application/json',
}
req = urllib.request.Request(url, json.dumps(data).encode(), headers)
# urllib.request.urlopen ではなく opener.open を使ってリクエストを投げる
res = opener.open(req)
body = res.read()
res.close()
# 以降、opener.open を使った場合は Cookie が保持されている
ここまでやるくらいなら requests とかを使ってもいいのでは?と思うが。
補足
HTTP API を叩くための最低限の使い方についてまとめたが、より詳細な使い方については公式ドキュメントを読むと良い。
- urllib パッケージを使ってインターネット上のリソースを取得するには — Python 3 ドキュメント
- 21.6. urllib.request — URL を開くための拡張可能なライブラリ — Python 3 ドキュメント
-
どうしても外部モジュールを使う必要がある場合はこういうことをする https://qiita.com/hoto17296/items/a579c333a8690c96a56c ↩
-
例えば何らかの Web サービスにログインした上でページの情報を取得したい場合など ↩