楼主: lastwinner

[参考文档] Python 研究(Dive Into Python)

[复制链接]
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
221#
 楼主| 发表于 2006-7-18 23:48 | 只看该作者
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 "<stdin>", 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 时并不做行为, 所以让我们来覆盖这些行为。 如何实现呢? 用一个自定义的头信息, 正如你处理 304 代码所做的。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
222#
 楼主| 发表于 2006-7-18 23:48 | 只看该作者
例 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。 如果你已经在配置文件或数据库中存储了这个地址, 就需要更新旧地址而不是反复地使用旧地址请求服务。 现在是更新你的地址簿的时候了。

同样的重定向处理也可以告诉你 不该 更新你的地址簿。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
223#
 楼主| 发表于 2006-7-18 23:48 | 只看该作者
例 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) 请求数据。 也许下一次它仍然被重定向, 也许不会。 也许会重定向到不同的地址。 这也不好说。 服务器说这个重定向仅仅是临时的, 你应该尊重它。 并且现在你获得了调用程序能尊重它的充分信息。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
224#
 楼主| 发表于 2006-7-18 23:48 | 只看该作者
11.8. 处理被压缩的数据
你要支持的最后一个重要的 HTTP 特性是压缩。 许多 web 服务具有发送压缩数据的能力, 这可以将网络线路上传输的大量数据消减 60% 以上。 尤其适用于 XML web 服务, 因为 XML 数据 的压缩率可以很高。

服务器不会为你发送押送数据,除非你告诉服务器你可以处理压缩数据。

例 11.14. 告诉服务器你想获得压缩数据
>>> import urllib2, httplib
>>> httplib.HTTPConnection.debuglevel = 1
>>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml')
>>> request.add_header('Accept-encoding', 'gzip')        
>>> opener = urllib2.build_opener()
>>> f = opener.open(request)
connect: (diveintomark.org, 80)
send: '
GET /xml/atom.xml HTTP/1.0
Host: diveintomark.org
User-agent: Python-urllib/2.1
Accept-encoding: gzip                                    
'
reply: 'HTTP/1.1 200 OK\r\n'
header: Date: Thu, 15 Apr 2004 22:24:39 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: Vary: Accept-Encoding
header: Content-Encoding: gzip                           
header: Content-Length: 6289                             
header: Connection: close
header: Content-Type: application/atom+xml
  这是关键: 一旦你已经创建了你的 Request 对象, 就添加一个 Accept-encoding 头信息告诉服务器你能接受 gzip 压缩数据。 gzip 是你使用的压缩算法的名称。 理论上你可以使用其它的压缩算法, 但是 gzip 是 web 服务器上使用率高达 99% 的一种。
  这是你的头信息传越网络线路的过程。
  这是服务器的返回信息: Content-Encoding: gzip 头信息意谓着你要回得的数据已经被 gzip 压缩了。
  Content-Length 头信息是已压缩数据的长度, 并非解压缩数据的长度。 一会儿你会看到, 实际的解压缩数据长度为 15955, 因此 gzip 压缩节省了 60% 以上的网络带宽!

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
225#
 楼主| 发表于 2006-7-18 23:49 | 只看该作者
例 11.15. 解压缩数据
>>> compresseddata = f.read()                              
>>> len(compresseddata)
6289
>>> import StringIO
>>> compressedstream = StringIO.StringIO(compresseddata)   
>>> import gzip
>>> gzipper = gzip.GzipFile(fileobj=compressedstream)      
>>> data = gzipper.read()                                 
>>> print data                                             
<?xml version="1.0" encoding="iso-8859-1"?>
<feed version="0.3"
  xmlns="http://purl.org/atom/ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xml:lang="en">
  <title mode="escaped">dive into mark</title>
  <link rel="alternate" type="text/html" href="http://diveintomark.org/"/>
  <-- rest of feed omitted for brevity -->
>>> len(data)
15955
  继续上面的例子, f 是一个从 URL 开启器返回的类似文件的对象。 使用它的 read() 方法将正常地获得非压缩数据, 但是因为这个数据已经被 gzip 压缩过, 所以这只是获得你想要的最终数据的第一步。
  OK, 只是先得有点儿凌乱的步骤。 Python 有 gzip 模块, 它读取 (并实际写入) 磁盘上的 gzip 压缩文件。 但是磁盘上还没有文件, 只在内存里有一个 gzip 压缩缓冲区, 并且 你不想仅仅为了解压缩而写出一个临时文件。 那么怎么做来创建一个内存数据 (compresseddata) 之外的类似文件的对象呢, 需要使用 StringIO 模块。 你首次看到 StringIO 模块是在 上一章, 但现在你会发现它的另一种用法。
  现在你可以创建 GzipFile 的一个实例, 并且告诉它其中的 “file” 是一个类似文件的对象 compressedstream。
  这是做所有工作的一行: 从 GzipFile 中 “读取” 将会解压缩数据。 感到奇妙吗? 是的, 它确实解压缩了数据。 gzipper 是一个类似文件的对象, 它代表一个 gzip 压缩文件。 尽管这个 “file” 并非一个磁盘上的真实文件; 但 gzipper 还是真正的从你用 StringIO 包装了压缩数据所创建的类似文件的对象中 “读取” 数据, 它仅仅是内存中的变量 compresseddata。 压缩的数据来自哪呢? 你通常从远程 HTTP 服务器下载, 然后从你用 urllib2.build_opener 创建的类似文件的对象中 “读取”。 令人吃惊吧, 这就是所有的步骤。 链条上的每一步都完全不知道上一步在造假。
  看看吧, 实际的数据 (实际为 15955 bytes)。

“等等!” 我听见你在叫。 “还能更简单吗!” 我知道你在想什么。 你在想那个 opener.open 返回一个类似文件的对象, 那么为什么不抛弃中间件 StringIO 而通过 f 直接访问 GzipFile 呢? OK, 或许你没想到, 但是别为此担心, 因为那样无法工作。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
226#
 楼主| 发表于 2006-7-18 23:49 | 只看该作者
例 11.16. 从服务器直接解压缩数据
>>> f = opener.open(request)                  
>>> f.headers.get('Content-Encoding')         
'gzip'
>>> data = gzip.GzipFile(fileobj=f).read()   
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "c:\python23\lib\gzip.py", line 217, in read
    self._read(readsize)
  File "c:\python23\lib\gzip.py", line 252, in _read
    pos = self.fileobj.tell()   # Save current position
AttributeError: addinfourl instance has no attribute 'tell'
  继续前面的例子, 你已经有一个用 Accept-encoding: gzip 头信息设置的 Request 对象。  
  简单地打开请求将获得你的头信息 (虽然还没下载任何数据)。 正如你从 Content-Encoding 头信息所看到的, 这个数据已经被 gzip 压缩发送了。
  从 opener.open 返回了一个类似文件的对象, 并且阅读头信息你可以获知, 你将获得 gzip 压缩数据, 为什么不简单地通过那个类似文件的对象直接访问 GzipFile 呢? 因为你从 GzipFile 实例 “读取” , 他将从远程 HTTP 服务器 “读取” 被压缩的数据并且立即解压缩。 这是个好主意, 但是不行的是它无法工作。 由 gzip 压缩的工作方式所致, GzipFile 需要存储其位置并在压缩文件上往返游走。当 “file” 是来自远程服务器的字节流时无法工作; 你能用它做的所有工作就是一次返回一个字节流, 而不是在字节流上往返。 所以使用 StringIO 这种看上去不雅的手段是最好的解决方案: 下载压缩的数据, 除此之外用 StringIO 创建一个类似文件的对象, 并从中解压缩数据。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
227#
 楼主| 发表于 2006-7-18 23:49 | 只看该作者
11.9. 全部放在一起
你已经看到了构造一个职能的 HTTP web 客户的所有片断。 现在让我们看看如何将它们整合到一起。

例 11.17. openanything 函数
这个函数定义在 openanything.py 中。


def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
    # non-HTTP code omitted for brevity
    if urlparse.urlparse(source)[0] == 'http':                                       
        # open URL with urllib2                                                     
        request = urllib2.Request(source)                                          
        request.add_header('User-Agent', agent)                                      
        if etag:                                                                    
            request.add_header('If-None-Match', etag)                                
        if lastmodified:                                                            
            request.add_header('If-Modified-Since', lastmodified)                    
        request.add_header('Accept-encoding', 'gzip')                                
        opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler())
        return opener.open(request)                                                  
  urlparse 是一个解析 URL 的垂手可得的工具模块。 它的主要功能也调用 urlparse, 获得一个 URL 并将其拆分为为一个包含 (scheme, domain, path, params, 查询串参数 和 验证片断) 的 tuple。 当然, 你唯一需要注意的就是 scheme, 确认你处理的是一个 HTTP URL (urllib2 才能处理)。
  通过调用函数使用 User-Agent 向 HTTP 服务器确定你的身份。 如果没有 User-Agent 被指定, 你会使用一个默认的, 就是定义在早期的 openanything.py 模块中的那个。 你从来不会使用到默认的定义在 urllib2 中的那个。
  如果给出了 ETag, 要在 If-None-Match 头信息中发送它。
  如果给出了最近修改日期, 要在 If-Modified-Since 头信息中发送它。
  如果可能要告诉服务器你要获取压缩数据。
  使用 两个 自定义 URL 处理器创建一个 URL 开启器: SmartRedirectHandler 为了处理 301 和 302 重定向, 而 DefaultErrorHandler 为了处理 304, 404 以及其它的错误条件。
  就这样! 打开 URL 并返回一个类似文件的对象给调用者。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
228#
 楼主| 发表于 2006-7-18 23:49 | 只看该作者
例 11.18. fetch 函数
这个函数定义在 openanything.py 中。


def fetch(source, etag=None, last_modified=None, agent=USER_AGENT):  
    '''Fetch data and metadata from a URL, file, stream, or string'''
    result = {}                                                      
    f = openAnything(source, etag, last_modified, agent)              
    result['data'] = f.read()                                         
    if hasattr(f, 'headers'):                                       
        # save ETag, if the server sent one                          
        result['etag'] = f.headers.get('ETag')                        
        # save Last-Modified header, if the server sent one         
        result['lastmodified'] = f.headers.get('Last-Modified')      
        if f.headers.get('content-encoding', '') == 'gzip':           
            # data came back gzip-compressed, decompress it         
            result['data'] = gzip.GzipFile(fileobj=StringIO(result['data']])).read()
    if hasattr(f, 'url'):                                             
        result['url'] = f.url                                       
        result['status'] = 200                                       
    if hasattr(f, 'status'):                                          
        result['status'] = f.status                                 
    f.close()                                                        
    return result                                                   
  首先, 你用 URL, ETag hash, Last-Modified 日期 和 User-Agent 调用 openAnything 函数。
  读取从服务器返回的真实数据。 这可能是被压缩的; 如果是, 将在后面进行解压缩。
  保存从服务器返回的 ETag hash, 所以调用程序下一次能通过它返回给你, 并且可以传递给 openAnything, 连同 If-None-Match 头信息一起发送给远程服务器。
  也要保存 Last-Modified 数据。
  如果服务器说它发送的是压缩数据, 就执行解压缩。
  如果你的服务器返回一个 URL 就保存它, 并在查明之前假定状态代码为 200。
  如果其中一个自定义 URL 处理器捕获了一个状态代码, 也要保存下来。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
229#
 楼主| 发表于 2006-7-18 23:50 | 只看该作者
例 11.19. 使用 openanything.py
>>> import openanything
>>> useragent = 'MyHTTPWebServicesApp/1.0'
>>> url = 'http://diveintopython.org/redir/example301.xml'
>>> params = openanything.fetch(url, agent=useragent)              
>>> params                                                         
{'url': 'http://diveintomark.org/xml/atom.xml',
'lastmodified': 'Thu, 15 Apr 2004 19:45:21 GMT',
'etag': '"e842a-3e53-55d97640"',
'status': 301,
'data': '<?xml version="1.0" encoding="iso-8859-1"?>
<feed version="0.3"
<-- rest of data omitted for brevity -->'}
>>> if params['status'] == 301:                                    
...     url = params['url']
>>> newparams = openanything.fetch(
...     url, params['etag'], params['lastmodified'], useragent)   
>>> newparams
{'url': 'http://diveintomark.org/xml/atom.xml',
'lastmodified': None,
'etag': '"e842a-3e53-55d97640"',
'status': 304,
'data': ''}                                                        
  真正第一次获取资源时, 你没有 ETag hash 或 Last-Modified 日期, 所以你不用使用这些参数。 (They're 可选参数。)
  你获得了一个 dictionary, 它包括几个有用的头信息, HTTP 状态代码 和 从服务器返回的真实数据。 openanything 在内部处理 gzip 压缩; 在本级别上你不必关心它。
  如果你得到一个 301 状态代码, 表示是个永久重定向, 你需要更新你的 URL 到新地址。
  第二次获取相同的资源, 你已经从以往获得了各种信息: URL (可能被更新了), 从上一次访问获得的 ETag, 从上一次访问获得的 Last-Modified 日期, 当然还有 User-Agent。
  你重新获取了这个 dictionary, 但是数据没有改变, 所以你得到了一个 304 状态代码而没有数据。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
230#
 楼主| 发表于 2006-7-18 23:50 | 只看该作者
11.10. 小结
openanything.py 及其函数现在可以完美地工作了。

每个客户都应该支持的 5 个 HTTP web 服务重要特性:

通过设置适当的 User-Agent 识别你的应用。
处理 适当的永久重定向。
支持 Last-Modified 日期检查 从而避免在数据未改变的情况下重新下载数据。
支持 ETag hash 从而避免在数据未改变的情况下重新下载数据。
支持 gzip 压缩 从而在数据 已经 改变的情况下尽可能地减少传输带宽。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表