delhi09の勉強日記

技術トピック専用のブログです。自分用のメモ書きの投稿が多いです。あくまで「勉強日記」なので記事の内容は鵜呑みにしないでください。

requestsが送出した例外からレスポンスボディを取得する

概要

requestsは以下のようにResponse.raise_for_status()というメソッドを使うとレスポンスのステータスコードが400系や500系だった場合に、例外を送出することができる。

bad_r = requests.get('http://httpbin.org/status/404')
bad_r.raise_for_status()

docs.python-requests.org

また、requestsが送出する例外は、全てrequests.exceptions.RequestExceptionを継承しているということが公式ドキュメントに明記されている。
docs.python-requests.org


従って、以下のようなコードを書くと、レスポンスが不正だった場合にエラー内容をログに出力することができる。

bad_r = requests.get("http://httpbin.org/status/404")
try:
    bad_r.raise_for_status()
except RequestException:
    logger.exception("request failed.")

しかし、上記のコードではスタックトレースには以下のようなログしか出力されない。

requests.exceptions.HTTPError: 404 Client Error: NOT FOUND for url: http://httpbin.org/status/404

これだけだと、例えばAPIが返したJSONのエラー情報を出力したいという場合に困る。
従って、レスポンスボディを出力できないのかと思い調べてみた。

結論

RequestExceptionはフィールドにResponseオブジェクトを保持しているので、以下のようにe.response.textで例外オブジェクトからレスポンスボディを取得することができる。

bad_r = requests.get("http://httpbin.org/status/404")
try:
    bad_r.raise_for_status()
except RequestException as e:
    logger.exception("request failed. error=(%s)", e.response.text)

※ 以下のRequestExceptionソースコードを読むと、フィールドにResponseオブジェクトを保存していることが分かる。
docs.python-requests.org

検証

以下の「Dog API」という公開APIを使ってみる。

dog.ceo

このAPIは例えば以下のような存在しないURLを叩くと404と一緒に以下のようなJSONのエラーメッセージが返ってくる。
https://dog.ceo/api/breeds/image/notfound

{"status":"error","message":"No route found for \"GET \/api\/breeds\/image\/notfound\" with code: 0","code":404}

このJSONを出力できることを確認する。

■検証用コード

import logging

import requests
from requests.exceptions import RequestException

logger = logging.getLogger(__name__)

response = requests.get("https://dog.ceo/api/breeds/image/notfound")
try:
    response.raise_for_status()
except RequestException as e:
    print(e.response.text)


■実行結果
コンソールに以下が出力された。

{"status":"error","message":"No route found for \"GET \/api\/breeds\/image\/notfound\" with code: 0","code":404}

以上