LoginSignup
1
1
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

urllib.requestのレスポンス:正常時とエラー時の違い(初心者向け)

Last updated at Posted at 2024-01-12

本記事は比較的初心者向けにPythonの標準モジュールであるurllib.requestの使い方について簡単にまとめたものです。
典型的なパターンとして、Pythonで標準モジュールのみを利用し外部APIへのアクセスを実現するケースを想定しています。

環境

$ python --version
Python 3.11.3

リクエスト

あるURLに対してGETアクセスするとします。
モジュール名は asrequest に短縮しています。

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

参考文献:

1
1
0

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
1
1