使用Python requests模块时的try/except
我在做一些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 个回答
你可以查看一下 response.status_code
这个值。如果它不是 200
,那么你就可以认为出现了错误,这时候可以自己抛出一个异常。
注意: 你应该使用 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
的状态码都成立。
如果你想让程序在遇到非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
并不是个好主意,因为它会捕获像 ConnectionError
或 TimeoutError
这样的错误,而这些错误并不代表你想要捕获的情况。