使用Python requests模块时的try/except

48 投票
3 回答
82688 浏览
提问于 2025-04-18 11:47

我在做一些API测试,想写一个函数,输入一个网址后,它会返回一个json格式的响应。如果返回的是HTTP错误,就会返回一个错误信息。

之前我用的是urllib2,现在想换成requests来用。不过我发现无论出现什么错误,我的异常处理部分似乎都没有被执行。

testURL = 'http://httpbin.org/status/404'


def return_json(URL):
    try:
        response = requests.get(URL)
        json_obj = response.json()
        return json_obj
    except requests.exceptions.HTTPError as e:
        return "Error: " + str(e)

我运行上面的代码后得到的结果是...

<Response [404]>

3 个回答

1

你可以查看一下 response.status_code 这个值。如果它不是 200,那么你就可以认为出现了错误,这时候可以自己抛出一个异常。

19

注意: 你应该使用 response.raise_for_status(),就像上面Ian的回答中提到的那样(他是requests模块的维护者之一)。


你如何处理这些情况,取决于你对HTTP错误的理解。虽然有状态码,但并不是所有非200的状态都意味着一定有错误。

正如你所注意到的,requests库把这些状态码视为HTTP响应的另一部分,并不会抛出异常。例如,HTTP状态302表示找到,但响应中没有响应体,而是有一个Location头部,你需要跟随这个头部才能到达你真正想要的资源。

所以你需要查看response.status_code,并根据这个状态码进行处理,同时用try..except来捕获实际的协议错误。在捕获这些错误时,你应该捕获requests.exceptions.RequestException,因为这是requests模块抛出的所有其他异常的基类

下面是一个示例,展示了三种情况:

  • 成功的200 OK响应
  • 请求和响应成功,但状态码不是200
  • 协议错误(无效的模式)
import requests

test_urls = ['http://httpbin.org/user-agent',
             'http://httpbin.org/status/404',
             'http://httpbin.org/status/500',
             'httpx://invalid/url']


def return_json(url):
    try:
        response = requests.get(url)

        # Consider any status other than 2xx an error
        if not response.status_code // 100 == 2:
            return "Error: Unexpected response {}".format(response)

        json_obj = response.json()
        return json_obj
    except requests.exceptions.RequestException as e:
        # A serious problem happened, like an SSLError or InvalidURL
        return "Error: {}".format(e)


for url in test_urls:
    print "Fetching URL '{}'".format(url)
    print return_json(url)
    print

输出:

Fetching URL 'http://httpbin.org/user-agent'
{u'user-agent': u'python-requests/2.1.0 CPython/2.7.1 Darwin/11.4.2'}

Fetching URL 'http://httpbin.org/status/404'
Error: Unexpected response <Response [404]>

Fetching URL 'http://httpbin.org/status/500'
Error: Unexpected response <Response [500]>

Fetching URL 'httpx://invalid/url'
Error: No connection adapters were found for 'httpx://invalid/url'

如果你得到了成功的响应,但它并不是JSON格式,response.json()也可能会抛出异常,所以你可能也需要考虑这一点。


注意: if not response.status_code // 100 == 2 这一部分的工作原理是这样的: //运算符进行所谓的向下取整除法,所以它会向下取整到下一个整数(在Python 2.x中,/的默认行为是这样的,但在Python 3.x中,/被改为进行浮点除法)。所以status // 100 == 2对于所有2xx的状态码都成立。

87

如果你想让程序在遇到非200状态码时抛出一个错误,可以使用 response.raise_for_status()。这样你的代码看起来会是:

testURL = 'http://httpbin.org/status/404'


def return_json(URL):
    response = requests.get(testURL)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError as e:
        # Whoops it wasn't a 200
        return "Error: " + str(e)

    # Must have been a 200 status code
    json_obj = response.json()
    return json_obj

你可以看到,这个方法明显比其他解决方案简单,而且不需要你手动检查状态码。你只需要捕获一个 HTTPError,因为 raise_for_status 会抛出这个错误。捕获 RequestsException 并不是个好主意,因为它会捕获像 ConnectionErrorTimeoutError 这样的错误,而这些错误并不代表你想要捕获的情况。

撰写回答