为什么 urllib2 的 .getcode() 方法在出现 404 时崩溃?

Why does urllib2's .getcode() method crash on 404's?

在初学者 Python 课程中,我参加了 Lynda,它说使用 .getcode() 从 url 获取 http 代码,并且可以在阅读之前用作测试数据:

webUrl = urllib2.urlopen('http://www.wired.com/tag/magazine-23-05/page/4')
print(str(webUrl.getcode()))
if (webURL.getcode() == 200):
    data = webURL.read()
else:
    print 'error'

但是,当与上面的 404 页面一起使用时,它会导致 Python 退出:Python function terminated unexpectedly: HTTP Error 404: Not Found,所以这节课似乎完全错误?

那么我的问题是 .getcode() 到底有什么用?您实际上不能使用它来测试 http 代码是什么,除非您知道它是什么(或者至少知道它不是 404)。课程有误还是我遗漏了什么?

我的理解是这样做的正确方法是这样的,它根本不使用 .getcode() (尽管告诉我是否有更好的方法):

try:
    url = urllib2.urlopen('http://www.wired.com/tag/magazine-23-05/page/4')
except urllib2.HTTPError, e:
    print e

这根本不使用 .getcode()。我是误解了 .getcode() 的意义还是它几乎没用?在专用于打开 url 的库中获取页面代码的方法对我来说似乎很奇怪,无法处理像返回 404 这样微不足道的事情。

urllib2 将 404 代码视为错误状态,因此会引发异常。异常对象支持getcode()方法:

>>> import urllib2
>>> try:
...     url = urllib2.urlopen('http://www.wired.com/tag/magazine-23-05/page/4')
... except urllib2.HTTPError, e:
...     print e
...     print e.getcode()
...
HTTP Error 404: Not Found
404

引发错误的事实记录很少。该库使用一堆处理程序来形成一个 URL 开启器(创建时包含 (urllib2.build_opener(), installed with urllib2.install_opener()), and in the default stack the urllib2.HTTPErrorProcessor class

正是 class 导致任何响应代码超出 2xx 范围的响应都被视为错误。然后 3xx 状态代码由 HTTPRedirectHandler object, and some of the 40x codes (related to authentication) are handled by specialised authentication handlers 处理,但大多数代码只是作为异常抛出。

如果您要安装额外的 Python 库,我建议您安装 requests library instead, where error handling is a lot saner. No exceptions are raised unless you explicitly request it:

import requests

response = requests.get(url)
response.raise_for_status()  # raises an exception for 4xx or 5xx status codes.

是的,您没看错,它会抛出非 "OK" http 状态代码的异常。在撰写本课程时可能有效,因为 URL 有效,但如果您现在在浏览器中尝试 URL,您也会得到 404 not found,因为 URL 现在不再有效。

在这种情况下,urllib2.urlopen is in a way (arguably), abusing exceptions to return http status codes as exceptions (see docs for urllib2.HTTPError)

顺便说一句,我建议尝试使用 requests 库,如果您打算在教程之外的 space 中进行一些实际的脚本编写工作,那么使用它会更好。