urllibだけである程度HTTPリクエストを頑張る

Pythonの標準ライブラリだけでWeb APIをコールするAWS Lambdaが作りたかったのでurllibを使ってみました。
2020.01.31

AWS LambdaからいくつかのWeb APIを呼び出すコードをrequestsなどの外部ライブラリを使わずにささっと書きたくて、標準ライブラリであるurllibを色々使ってみたのでそのメモです。

使ったもの

  • Python: 3.7.6
  • ライブラリ: urllib

GET

GETの場合はurlopenに直接URLを指定するか、下記のようにRequestを生成します。

認証ヘッダーなどつけたい場合がほとんどだと思うのでRequestを使ってみます。

from urllib import request, parse
import json
from pprint import pprint

## GET

get_header = {'Authorization': 'XXXXXXXXXX'}
get_req = request.Request('https://httpbin.org/get', headers=get_header)

with request.urlopen(get_req, headers=get_header) as res:
  # resは http.client.HTTPResponse
  body = json.loads(res.read()) # レスポンスボディ
  headers = res.getheaders() # ヘッダー(dict)
  status = res.getcode() # ステータスコード
  pprint(headers)
  pprint(status)
  pprint(body)

POST

POSTに限らずGET以外のHTTPメソッドの場合はmethodパラメータを指定します。 そしてリクエストボディはdataで指定します。ドキュメントによるとこのパラメータは・・・

The supported object types include bytes, file-like objects, and iterables.

ということなので、stringの場合はencodeしてbytesに変換します。formの送信の場合は下記のようになります。 JSONをPOSTする場合は同様にJSON文字列をencodeします。

## POST(form)

post_form_data = parse.urlencode({'key': 'value'})
post_form_headers = {'content-type': 'application/x-www-form-urlencoded'}
post_req = request.Request('https://httpbin.org/post', data=post_form_data.encode(), method='POST')

with request.urlopen(post_req) as res:
  body = json.loads(res.read()) # レスポンスボディ
  pprint(body)

Basic認証

Basic認証もライブラリなしで頑張ってみます。

ハンドラを使った方法もサポートされているのですが、ズボラなのでヘッダーを組み立ててみました。

## BASIC認証
import base64

basic_auth_token = base64.b64encode('user:password'.encode()).decode()
basic_auth_header = {'Authorization': 'Basic ' + basic_auth_token}
basic_auth_req = request.Request('https://httpbin.org/basic-auth/user/password', headers=basic_auth_header)

with request.urlopen(basic_auth_req) as res:
  body = json.loads(res.read()) # レスポンスボディ
  pprint(body)

レスポンスステータスが4XX, 5XXの時

urllib.error.HTTPErrorがスローされるので必要に応じてハンドリングします。 下記の例ではいずれもHTTPErrorがスローされてexcept内が実行されます。

from urllib import request, parse, error

for code in [300, 400, 500]:
  req = request.Request('https://httpbin.org/status/{}'.format(code))
  try:
    with request.urlopen(req) as res:
      pprint(res.getcode())
  except error.HTTPError as e:
    pprint(e)

まとめ

urllibだけでHTTPリクエストを頑張ってみました。