http://www.woodpecker.org.cn/diveintopython/toc/index.html
11.7. 处理重定向
你可以使用两种不同的自定义 URL 处理器来处理永久重定向和临时重定向。
首先,让我们来看看重定向处理的必要性。
例 11.10. 没有重定向处理的情况下,访问 web 服务
>>> import urllib2, httplib >>> httplib.HTTPConnection.debuglevel = 1 >>> request = urllib2.Request( ... 'http://diveintomark.org/redir/example301.xml') >>> opener = urllib2.build_opener() >>> f = opener.open(request) connect: (diveintomark.org, 80) send: ' GET /redir/example301.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 301 Moved Permanently\r\n' header: Date: Thu, 15 Apr 2004 22:06:25 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Location: http://diveintomark.org/xml/atom.xml header: Content-Length: 338 header: Connection: close header: Content-Type: text/html; charset=iso-8859-1 connect: (diveintomark.org, 80) send: ' GET /xml/atom.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 200 OK\r\n' header: Date: Thu, 15 Apr 2004 22:06:25 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Last-Modified: Thu, 15 Apr 2004 19:45:21 GMT header: ETag: "e842a-3e53-55d97640" header: Accept-Ranges: bytes header: Content-Length: 15955 header: Connection: close header: Content-Type: application/atom+xml >>> f.url 'http://diveintomark.org/xml/atom.xml' >>> f.headers.dict {'content-length': '15955', 'accept-ranges': 'bytes', 'server': 'Apache/2.0.49 (Debian GNU/Linux)', 'last-modified': 'Thu, 15 Apr 2004 19:45:21 GMT', 'connection': 'close', 'etag': '"e842a-3e53-55d97640"', 'date': 'Thu, 15 Apr 2004 22:06:25 GMT', 'content-type': 'application/atom+xml'} >>> f.status Traceback (most recent call last): File "", line 1, in ? AttributeError: addinfourl instance has no attribute 'status'
你最好开启调试状态,看看发生了什么。 | |
这是一个我已经设置了永久重定向到我的 Atom feed http://diveintomark.org/xml/atom.xml 的 URL。 | |
毫无疑问,当你试图从那个地址下载数据时,服务器会返回 301 状态代码,告诉你你访问的资源已经被永久移动了。 | |
服务器同时返回 Location: 头信息,它给出了这个数据的新地址。 | |
urllib2 注意到了重定向状态代码并会自动从Location: 头信息中给出的新地址获取数据。 | |
从 opener 返回的对象包括新的永久地址和第二次请求获得的所有头信息 (从一个新的永久地址获得)。但是状态代码不见了,因此你无从知晓重定向到底是永久重定向还是临时重定向。这是至关重要的:如果这是临时重定向,那么你应 该继续使用旧地址访问数据。但是如果是永久重定向 (正如本例),你应该从现在起使用新地址访问数据。 |
这不太理想,但很容易改进。实际上当 urllib2 遇到 301 或 302 时的行为并不是我们所期望的,所以让我们来覆盖这些行为。如何实现呢?用一个自定义的处理器,。
例 11.11. 定义重定向处理器
这个类定义在 openanything.py。
class SmartRedirectHandler(urllib2.HTTPRedirectHandler): def http_error_301(self, req, fp, code, msg, headers): result = urllib2.HTTPRedirectHandler.http_error_301( self, req, fp, code, msg, headers) result.status = code return result def http_error_302(self, req, fp, code, msg, headers): result = urllib2.HTTPRedirectHandler.http_error_302( self, req, fp, code, msg, headers) result.status = code return result
重定向行为定义在 urllib2 的一个叫做 HTTPRedirectHandler 的类中。我们不想完全地覆盖这些行为,只想做点扩展,所以我们子类化 HTTPRedirectHandler,从而我们仍然可以调用祖先类来实现所有原来的功能。 | |
当从服务器获得 301 状态代码,urllib2 将搜索处理器并调用 http_error_301 方法。我们首先要做的就是在祖先中调用 http_error_301 方法,它将处理查找 Location: 头信息的工作并跟踪重定向到新地址。 | |
这是关键:返回之前,你存储了状态代码 (301),所以主调程序稍后就可以访问它了。 | |
临时重定向 (状态代码 302) 以相同的方式工作:覆盖 http_error_302 方法,调用祖先,并在返回之前保存状态代码。 |
这将为我们带来什么?现在你可以用自定义重定向处理器构造一个的 URL 开启器,并且它依然能自动跟踪重定向,也能展示出重定向状态代码。
例 11.12. 使用重定向处理器检查永久重定向
>>> request = urllib2.Request('http://diveintomark.org/redir/example301.xml') >>> import openanything, httplib >>> httplib.HTTPConnection.debuglevel = 1 >>> opener = urllib2.build_opener( ... openanything.SmartRedirectHandler()) >>> f = opener.open(request) connect: (diveintomark.org, 80) send: 'GET /redir/example301.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 301 Moved Permanently\r\n' header: Date: Thu, 15 Apr 2004 22:13:21 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Location: http://diveintomark.org/xml/atom.xml header: Content-Length: 338 header: Connection: close header: Content-Type: text/html; charset=iso-8859-1 connect: (diveintomark.org, 80) send: ' GET /xml/atom.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 200 OK\r\n' header: Date: Thu, 15 Apr 2004 22:13:21 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Last-Modified: Thu, 15 Apr 2004 19:45:21 GMT header: ETag: "e842a-3e53-55d97640" header: Accept-Ranges: bytes header: Content-Length: 15955 header: Connection: close header: Content-Type: application/atom+xml >>> f.status 301 >>> f.url 'http://diveintomark.org/xml/atom.xml'
首先,用刚刚定义的重定向处理器创建一个 URL 开启器。 | |
你发送了一个请求,并在响应中获得了 301 状态代码。 如此一来,http_error_301 方法就被调用了。你调用了祖先类,跟踪了重定向并且发送了一个新地址 (http://diveintomark.org/xml/atom.xml) 请求。 | |
这是决定性的一步:现在,你不仅做到了访问一个新 URL,而且获得了重定向的状态代码,所以你可以断定这是一个永久重定向。下一次你请求这个数据时,就应该使用 f.url 指定的新地址 (http://diveintomark.org/xml/atom.xml)。如果你已经在配置文件或数据库中存储了这个地址,就需要更新旧地址而不是反复地使用旧地址请求服务。现在是更新你的地址簿的时候了。 |
同样的重定向处理也可以告诉你不该 更新你的地址簿。
例 11.13. 使用重定向处理器检查临时重定向
>>> request = urllib2.Request( ... 'http://diveintomark.org/redir/example302.xml') >>> f = opener.open(request) connect: (diveintomark.org, 80) send: ' GET /redir/example302.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 302 Found\r\n' header: Date: Thu, 15 Apr 2004 22:18:21 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Location: http://diveintomark.org/xml/atom.xml header: Content-Length: 314 header: Connection: close header: Content-Type: text/html; charset=iso-8859-1 connect: (diveintomark.org, 80) send: ' GET /xml/atom.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 200 OK\r\n' header: Date: Thu, 15 Apr 2004 22:18:21 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Last-Modified: Thu, 15 Apr 2004 19:45:21 GMT header: ETag: "e842a-3e53-55d97640" header: Accept-Ranges: bytes header: Content-Length: 15955 header: Connection: close header: Content-Type: application/atom+xml >>> f.status 302 >>> f.url http://diveintomark.org/xml/atom.xml
这是一个 URL,我已经设置了它,让它告诉客户端临时 重定向到 http://diveintomark.org/xml/atom.xml。 | |
服务器返回 302 状态代码,标识出一个临时重定向。数据的临时新地址在 Location: 头信息中给出。 | |
urllib2 调用你的 http_error_302 方法,它调用了 urllib2.HTTPRedirectHandler 中的同名的祖先方法,跟踪重定向到一个新地址。然后你的 http_error_302 方法存储状态代码 (302) 使主调程序在稍后可以获得它。 | |
此时,已经成功追踪重定向到 http://diveintomark.org/xml/atom.xml。f.status 告诉你这是一个临时重定向,这意味着你应该继续使用原来的地址 (http://diveintomark.org/redir/example302.xml) 请求数据。也许下一次它仍然被重定向,也许不会。也许会重定向到不同的地址。这也不好说。服务器说这个重定向仅仅是临时的,你应该尊重它。并且现在你获得了能使主调程序尊重它的充分信息。 |