为什么Python的urllib2.urlopen()会为成功的状态代码引发HTTPError?

时间:2011-08-11 21:11:18

标签: python urllib2 http-status-codes

根据the urllib2 documentation

  

由于默认处理程序处理重定向(300范围内的代码),并且100-299范围内的代码表示成功,因此通常只能看到400-599范围内的错误代码。

然而以下代码

request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request)

使用代码201(已创建)引发HTTPError:

ERROR    2011-08-11 20:40:17,318 __init__.py:463] HTTP Error 201: Created

那么为什么urllib2会在此成功请求中抛出HTTP错误?

这不是太痛苦;我可以轻松地将代码扩展到:

try:
    request = urllib2.Request(url, data, headers)
    response = urllib2.urlopen(request)
except HTTPError, e:
    if e.code == 201:
        # success! :)
    else:
        # fail! :(
else:
    # when will this happen...?

但这似乎不是预期的行为,基于文档和我无法找到关于这种奇怪行为的类似问题的事实。

此外,else块应该期待什么?如果成功的状态代码都被解释为HTTPError s,那么urllib2.urlopen()何时返回正常的文件类响应对象,如所有urllib2文档引用?

3 个答案:

答案 0 :(得分:16)

您可以编写自定义Handler类,以便与urllib2一起使用,以防止将特定错误代码引发为HTTError。这是我以前用过的一个:

class BetterHTTPErrorProcessor(urllib2.BaseHandler):
    # a substitute/supplement to urllib2.HTTPErrorProcessor
    # that doesn't raise exceptions on status codes 201,204,206
    def http_error_201(self, request, response, code, msg, hdrs):
        return response
    def http_error_204(self, request, response, code, msg, hdrs):
        return response
    def http_error_206(self, request, response, code, msg, hdrs):
        return response

然后你就可以使用它:

opener = urllib2.build_opener(self.BetterHTTPErrorProcessor)
urllib2.install_opener(opener)

req = urllib2.Request(url, data, headers)
urllib2.urlopen(req)

答案 1 :(得分:3)

正如实际的图书馆文档所述:

  

对于200个错误代码,立即返回响应对象。

     

对于非200错误代码,这只是通过OpenerDirector.error()将作业传递给protocol_error_code处理程序方法。最终,如果没有其他处理程序处理错误,urllib2.HTTPDefaultErrorHandler将引发HTTPError。

http://docs.python.org/library/urllib2.html#httperrorprocessor-objects

答案 2 :(得分:0)

我个人认为这是默认行为,这是一个错误并且非常不直观。 确实,非2XX代码暗含协议级别错误,但将其转换为异常实在是太遥远了(至少在我看来)。

无论如何,我认为避免这种情况的最优雅的方法是:

opener = urllib.request.build_opener()
for processor in opener.process_response['https']: # or http, depending on what you're using
   if isinstance(processor, urllib.request.HTTPErrorProcessor): # HTTPErrorProcessor also for https
       opener.process_response['https'].remove(processor)
       break # there's only one such handler by default
response = opener.open('https://www.google.com')

现在您有了响应对象。您可以检查其状态码,标题,正文等。