LoginSignup
455

More than 3 years have passed since last update.

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

Last updated at Posted at 2018-02-28

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 例外が投げられるため、例外処理を書く必要がある。(後述)

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 を先に書く必要がある。

セッション (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 を叩くための最低限の使い方についてまとめたが、より詳細な使い方については公式ドキュメントを読むと良い。


  1. どうしても外部モジュールを使う必要がある場合はこういうことをする https://qiita.com/hoto17296/items/a579c333a8690c96a56c 

  2. 例えば何らかの Web サービスにログインした上でページの情報を取得したい場合など 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
455