本記事は比較的初心者向けにPythonの標準モジュールであるurllib.requestの使い方について簡単にまとめたものです。
典型的なパターンとして、Pythonで標準モジュールのみを利用し外部APIへのアクセスを実現するケースを想定しています。
環境
$ python --version
Python 3.11.3
リクエスト
あるURLに対してGETアクセスするとします。
モジュール名は as
で request
に短縮しています。
import urllib.request as request
URL = "https://xxxx"
try:
req = request.Request(
URL,
# TODO: 必要に応じてヘッダー情報を追加
headers={},
method="GET",
)
with request.urlopen(req) as res:
print("レスポンスコード: ", res.getcode())
decoded_res = res.read().decode("utf-8")
# TODO: データがJSON形式以外の場合は別のデータ処理を書く
if decoded_res:
data = json.loads(decoded_res)
# 以下略:レスポンスボディデータを活用し必要な処理
xss
except Exception as err:
raise err
上の書き方のポイント:
- HTTPアクセスで400/500などエラーが起きたときは、後に説明するリターンオブジェクトの違いにより
with
以下の処理に失敗します。その対策としてtry/exceptでエラーを拾えるようにしています - HTTPアクセスのレスポンスボディがない場合、
json.loads()
でエラーが出るのでif分岐を書いています - レスポンスボディがJSONオブジェクトでない場合、つまりレスポンスヘッダーの
"Content-Type"
が"application/json" 以外の場合は
json.loads()とは別の処理が必要です
レスポンス
上のコードをベースに print()
を追加してみます。
出力結果でリクエスト結果の正常時とエラー時でオブジェクトが異なることが確認できます。
req = request.Request(
URL,
headers={},
method="GET",
)
print(req)
print(type(req))
正常時
オブジェクトクラスは http.client.HTTPResponse
です。
<http.client.HTTPResponse object at 0x1064d3820>
<class 'http.client.HTTPResponse'>
なお、レスポンスボディはこのままではPythonでデータとして扱えないので、デコードする必要があります。
エラー時
オブジェクトクラスは urllib.request.Request
です。中身はリクエストで投げた内容です。
<urllib.request.Request object at 0x104828b50>
<class 'urllib.request.Request'>
正常なレスポンスの中身
print()
を追加してみます。
URL = "https://xxxx"
req = request.Request(
URL,
# TODO: 必要に応じてヘッダー情報を追加
headers={},
method="GET",
)
with request.urlopen(req) as res:
print("レスポンスコード: ", res.status)
print("URL: ", res.url)
print(res.headers)
レスポンスコード: 200
URL: https://xxxx
Date: Fri, 12 Jan 2024 21:38:55 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: close
Cache-Control: no-cache
以下略
レスポンスコードのチェックのみしたい場合などに良さそうです。
追記: レスポンスオブジェクトに対して利用できる以下のメソッドはPythonバージョン3.9以降非推奨とのことです(公式)。
code
getcode()
geturl()
info()
responseオブジェクトのデコード(JSONの場合)
レスポンスのオブジェクトタイプは以下の変換の手順を追ってPythonで扱える型に変えます。
http.client.HTTPResponse型
↓
bytes型
↓
str型
↓
dict/array型
http.client.HTTPResponse型 → bytes型
with request.urlopen(req) as res:
res = res.read()
(print(type(res))
print(res)
<class 'bytes'>
b'{"apiKey":"xxxx", ... }'
bytes型 → str型
utf-8
を指定し日本語などの文字化けを防ぎます。
res = res.read().decode("utf-8")
print(res)
print(type(res))
<class 'str'>
{"apiKey":"xxxx", ... }
str型 → dict/array型
dict型の例です。
先述のように、HTTPアクセスのレスポンスボディがない場合、 json.loads()
でエラーが出るのでif分岐を書いています。
res = res.read().decode("utf-8")
if res:
res = json.loads(res)
print(type(res))
print(res)
{'apiKey': 'xxxx', ...}
<class 'dict'>
エラークラス
以下のモジュールをインポートすることで、エラークラスが使えます。
import urllib.error
以下では as
でモジュール名を変えています。
import urllib.request as request
import urllib.error as request_error
URL = "https://xxxx"
try:
req = request.Request(
URL,
headers={},
method="GET",
)
with request.urlopen(req) as res:
# 以下略
# HTTPステータスコードが 400 or 500
except request_error.HTTPError as err:
raise err
# その他HTTP通信の失敗
except request_error.URLError as err:
raise err
except Exception as err:
raise err
また、この場合エラー内容の一部を取り出せます。
except request_error.HTTPError as err:
print(err)
print("エラーコード: ", err.code)
print("エラー原因: ", err.reason)
HTTP Error 400: Bad Request
エラーコード: 400
エラー原因: Bad Request
参考文献: