楼主: 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
211#
 楼主| 发表于 2006-7-18 23:45 | 只看该作者
11.3.2. 重定向 (Redirects)
有时资源移来移去。 Web 站点重组内容, 页面移动到了新的地址。甚至是 web 服务重组。 原来位于 http://example.com/index.xml 的 feed 汇聚可能被移动到 http://example.com/xml/atom.xml。 或者因为一个机构的扩展或重组,整个域被迁移。 例如, http://www.example.com/index.xml 可能被重定向到 http://server-farm-1.example.com/index.xml。

您每次从 HTTP 服务器请求任何类型的资源时, 服务器的响应中均包含一个状态代码。 状态代码 200 的意思是 “一切正常, 这就是您请求的页面”。 状态代码 404 的意思是 “页面没找到”。 (当浏览 web 时,你可能看到过 404 errors。)

HTTP 有两种不同的方法表示资源已经被移动。 状态代码 302 表示 临时重定向; 这意谓着 “哎呀, 访问内容被临时移动” (然后在 Location: 头部给出临时地址)。 状态代码 301 表示 永久重定向; 这意谓着 “哎呀, 访问内容被永久移动” (然后在 Location: 头部给出新地址)。 如果您获得了一个 302 状态代码和一个新地址, HTTP 规范说您应该使用新地址获取您的请求, 但是下次您要访问同一资源时, 应该使用就地址重试。 但是如果您获得了一个 301 状态代码和一个新地址, 您应该从次使用新地址。

当从 HTTP 服务器接受到一个适当的状态代码时, urllib.urlopen 将自动 “跟踪” 重定向, 但不幸的是, 当它做了重定向时不会告诉你。你将最终获得所请求的数据,却丝毫不会察觉到在这个过程中一个潜在的库 “帮助” 你做了一次重定向操作。因此你将继续不断的使用旧地址, 并且每次都将获得被重定向的新地址。这一过程要往返两次而不是一次: 太没效率了! 本章的后面, 您将看到如何改进这一点,从而适当的且有效率的处理永久重定向。

使用道具 举报

回复
论坛徽章:
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
212#
 楼主| 发表于 2006-7-18 23:46 | 只看该作者
11.3.3. Last-Modified/If-Modified-Since
有些数据随时都在变化。 CNN.com 的主页经常几分钟就更新。 另一方面, Google.com 的主页几个星期才更新一次 (当他们上传特殊的假日 logo, 或为一个新服务作广告时)。 Web 服务是不变的:通常服务器指导你所请求的数据的最后修改时间,并且 HTTP 为服务器提供了一种将最近修改数据连同你请求的数据一同发送的方法。

如果你第二次 (或第三次, 或第四次) 请求相同的数据, 你可以告诉服务器你上一次获得的最后修改日期: 在你的请求中发送了一个 If-Modified-Since 头信息, 它包含了上一次从服务器连同数据所获得的日期。 如果数据从那时起没有改变, 服务器将返回一个特殊的 HTTP 状态代码 304, 这意谓着 “从上一次请求后这个数据没有改变”。 为什么这一点有何进步呢? 因为当服务器发送状态编码 304 时, 不再重新发送数据。 您仅仅获得了这个状态代码。 所以当数据没有更新时,你不需要一次又一次地下载相同的数据; 服务器假定你有本地的缓存数据。

所有现代的浏览器都支持最近修改的数据检查。如果你曾经访问过某页, 一天后重新访问相同的页时发现它没有变化, 并奇怪第二次访问时页面加载得如此之快 -- 这就是原因所在。 你的浏览器首次访问时会在本地缓存页面内容, 当你第二次访问, 浏览器自动发送 首次访问时从服务器获得的最近修改日期。 服务器简单的返回 304: 没有修改, 因此浏览器就会知道从本地缓存加载页面。 在这一点上,Web 服务也如此智能。

Python 的 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
213#
 楼主| 发表于 2006-7-18 23:46 | 只看该作者
11.3.4. ETag/If-None-Match
ETag 是实现与最近修改数据检查同样的功能的另一种方法: 没有变化时不重新下载数据。 其工作原理是: 服务器发送你所请求的数据的同时,发送某种数据的 hash (在 ETag 头信息中) 。 hash 的确定完全取决于服务器。 当第二次请求相同的数据时, 在 If-None-Match: 头信息中将包含 ETag hash, 如果数据没有改变, 服务器将返回 304 状态代码。 与最近修改数据检查相同, 服务器 仅仅 发送 304 状态代码; 第二次将不为你发送相同的数据。 在第二次请求时, 通过包含 ETag hash, 你会告诉服务器,如果 hash 仍旧匹配就没有必要重新发送相同的数据, 因为你还有上一次访问过的数据。

Python 的 URL 库没有对 ETag 的内置支持, 但是在本章后面你将看到如何添加这种支持。

使用道具 举报

回复
论坛徽章:
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
214#
 楼主| 发表于 2006-7-18 23:46 | 只看该作者
11.3.5. 压缩 (Compression)
最后一个重要的 HTTP 特性是 gzip 压缩。当谈论 HTTP web 服务时, 几乎总是会谈及在网络线路上传输的 XML。XML 是文本, 而且还是相当冗长的文本, 并且文本通常可以被很好地压缩。当你通过 HTTP 请求一个资源时, 可以告诉服务器, 如果它有任何新数据要发送给我时, 请以压缩的格式发送。 在你的请求中包含 Accept-encoding: gzip 头信息, 如果服务器支持压缩, 他将返回由 gzip 压缩的数据并且使用 Content-encoding: gzip 头信息标记。

Python 的 URL 库本身没有内置对 gzip 压缩的支持, 但是你能为请求添加任意的头信息。 Python 还提供了一个独立的 gzip 模块, 它提供了对数据进行解压缩的功能。

注意我们用于下载 feed 汇聚的 小单行脚本 并不支持任何这些 HTTP 特性。 让我们来看看如何改善他。

使用道具 举报

回复
论坛徽章:
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
215#
 楼主| 发表于 2006-7-18 23:46 | 只看该作者
11.4. 调试 HTTP web 服务
首先, 让我们开启 Python HTTP 库的调试特性并查看网络线路上的传输过程。 这对本章的全部内容都很有用, 因为你将添加越来越多的特性。

例 11.3. 调试 HTTP
>>> import httplib
>>> httplib.HTTPConnection.debuglevel = 1            
>>> import urllib
>>> feeddata = urllib.urlopen('http://diveintomark.org/xml/atom.xml').read()
connect: (diveintomark.org, 80)                       
send: '
GET /xml/atom.xml HTTP/1.0                           
Host: diveintomark.org                                
User-agent: Python-urllib/1.15                        
'
reply: 'HTTP/1.1 200 OK\r\n'                          
header: Date: Wed, 14 Apr 2004 22:27:30 GMT
header: Server: Apache/2.0.49 (Debian GNU/Linux)
header: Content-Type: application/atom+xml
header: Last-Modified: Wed, 14 Apr 2004 22:14:38 GMT  
header: ETag: "e8284-68e0-4de30f80"                  
header: Accept-Ranges: bytes
header: Content-Length: 26848
header: Connection: close
  urllib 依赖于另一个 Python 的标准库, httplib。 通常你不必显示地给出 import httplib (urllib 会自动导入), 但是你可以为 urllib 使用内部的 HTTPConnection 类设置调试标记来访问 HTTP 服务器。 这是一种令人难以置信的有用技术。 Python 其他的一些库也有类似的调试标记, 但是没有命名和开启他们的特殊标准; 如果有类似的特性可用,你需要阅读每一个库的文档来查看使用方法。
  既然已经设置了调试标记, HTTP 的请求和响应信息会实时地被打印出来。 首先告诉你的是你连接服务器 diveintomark.org 的 80 端口, 这是 HTTP 的标准端口。
  当你请求 Atom feed 时, urllib 向服务器发送三行信息。 第一行指出你使用的 HTTP verb, 和资源的路径 (除去域名)。 在本章中所有的请求都将使用 GET, 但是在下一章的 SOAP 中, 你会看到所有的请求都使用 POST 。除了请求的动词不同之外, 基本的语法是相同的。
  第二行是 Host 头信息, 它指出你所访问的服务的域名。 这一点很重要, 因为一个独立的 HTTP 服务器可以服务于多个不同的域。 当前我的服务器服务于 12 个域; 其他的服务器可以服务于成百乃至上千个域。
  第三行是 User-Agent 头信息。 在此你看到的是由 urllib 库默认添加的普通的 User-Agent 。 在下一节, 你会看到如何自定义它的更多细节。
  服务器用状态代码和一系列的头信息答复 (并且一些数据可能会被存储到 feeddata 变量中)。 这里的状态代码是 200, 意谓着 “一切正常, 这就是您请求的数据”。 服务器也会告诉你响应请求的数据, 一些有关服务器自身的信息, 传给你的数据的内容类型。 根据你的应用不同, 这或许有用, 或许没用。 这充分确认了你所请求的是一个 Atom feed, 瞧, 你获得了 Atom feed (application/atom+xml, 它是已经注册的有关 Atom feeds 的内容类型)。
  当此 Atom feed 有最近的修改, 服务器会告诉你 (本例中, 大约发生在 13 分钟之前)。 当下次请求同样的 feed 时,你可以这个日期再发送给服务器, 服务器将做最近修改数据检查。
  服务器也会告诉你这个 Atom feed 有一个值为 "e8284-68e0-4de30f80" 的 ETag hash。 这个 hash 自身没有任何意义; 除了在下次访问相同的 feed 时将他送还给服务器之外, 你也不需要用它做什么。 然后服务器使用它告诉你修改日期是否被改变了。

使用道具 举报

回复
论坛徽章:
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
216#
 楼主| 发表于 2006-7-18 23:46 | 只看该作者
11.5. 设置 User-Agent
改善你的 HTTP web 服务客户的第一步就是用 User-Agent 适当地鉴别你自己。为了做到这一点, 你需要远离基本的 urllib 而深入到 urllib2。

例 11.4. urllib2 介绍
>>> import httplib
>>> httplib.HTTPConnection.debuglevel = 1                             
>>> import urllib2
>>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml')
>>> opener = urllib2.build_opener()                                   
>>> feeddata = opener.open(request).read()                           
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: Wed, 14 Apr 2004 23:23:12 GMT
header: Server: Apache/2.0.49 (Debian GNU/Linux)
header: Content-Type: application/atom+xml
header: Last-Modified: Wed, 14 Apr 2004 22:14:38 GMT
header: ETag: "e8284-68e0-4de30f80"
header: Accept-Ranges: bytes
header: Content-Length: 26848
header: Connection: close
  如果你的 Python IDE 仍旧为上一节的例子而打开着, 你可以略过这一步, 在开启 HTTP 调试 中你能看到网络线路上的实际传输过程。
  使用 urllib2 获取 HTTP 资源包括三个处理步骤, 这会有助于你理解这一过程。第一步是创建 Request 对象, 它接受一个你最终想要获取资源的 URL。 注意这一步实际上还不能获取任何东西。
  第二步是创建一个 URL 开启器 (opener)。 这可以使用任何数量的操作者来控制响应的处理。 但你也可以创建一个没有任何自定义处理的开启器, 这就是这里的操作方式。 你将在本章后面探究重定向的部分看到如何定义和使用自定义操作者的内容。
  最后一个步骤是, 使用你创建的 Request 对象告诉开启器打开 URL。 因为你能从获得的信息中看到所有调试信息, 这个步骤实际上获得了资源并且把返回数据存储在了 feeddata 中。

使用道具 举报

回复
论坛徽章:
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
217#
 楼主| 发表于 2006-7-18 23:47 | 只看该作者
例 11.5. 用 Request 添加头信息
>>> request                                                
<urllib2.Request instance at 0x00250AA8>
>>> request.get_full_url()
http://diveintomark.org/xml/atom.xml
>>> request.add_header('User-Agent',
...     'OpenAnything/1.0 +http://diveintopython.org/')   
>>> feeddata = opener.open(request).read()                 
connect: (diveintomark.org, 80)
send: '
GET /xml/atom.xml HTTP/1.0
Host: diveintomark.org
User-agent: OpenAnything/1.0 +http://diveintopython.org/   
'
reply: 'HTTP/1.1 200 OK\r\n'
header: Date: Wed, 14 Apr 2004 23:45:17 GMT
header: Server: Apache/2.0.49 (Debian GNU/Linux)
header: Content-Type: application/atom+xml
header: Last-Modified: Wed, 14 Apr 2004 22:14:38 GMT
header: ETag: "e8284-68e0-4de30f80"
header: Accept-Ranges: bytes
header: Content-Length: 26848
header: Connection: close
  继续前面的例子; 你已经用你要访问的 URL 创建了 Request 。
  使用Request 对象的 add_header 方法, 你能向请求中添加任意的 HTTP 头信息。 第一个参数是头信息, 第二个参数是头信息的值。 User-Agent 的协商指令应该使用如下的特殊格式: 应用名, 跟一个斜线, 跟版本号。 剩下的是自由的格式, 你将看到许多疯狂的变化, 但通常这里应该包含你的应用的 URL。 The User-Agent 通常要记录经过服务器的连同你的请求的其他详细信息, 包含你的应用的 URL , 如果发生错误, 允许服务器管理员通过查看他们的访问日志与你联系。
  之前你创建的opener 对象也可以再生, 且它将再次获得相同的 feed, 但是使用了你自定义的 User-Agent 头信息。
  这就是你发送的自定义的 User-Agent, 代替了 Python 默认发送的一般的 User-Agent。 若你继续看,会注意到你定义的 User-Agent 头信息, 你实际上发送了一个 User-agent 头信息。 看看有何不同? urllib2 改变了大小写所以只有首字母是大写的。 这没问题,因为 HTTP 规定 头子段名完全是大小写无关的。

使用道具 举报

回复
论坛徽章:
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
218#
 楼主| 发表于 2006-7-18 23:47 | 只看该作者
11.6. 处理 Last-Modified 和 ETag
既然你知道如何在你的 web 服务请求中添加自定义的 HTTP 头信息, 接下来看看添加 Last-Modified 和 ETag 头信息的支持。

下面的这些例子将以调试标记置为关闭的状态来显示输出结果。 如果你还停留在上一部分的开启状态, 可以使用 httplib.HTTPConnection.debuglevel = 0 将其设置为关闭状态。 或者, 如果你认为有帮助也可以保持为开启状态。

例 11.6. 测试 Last-Modified
>>> import urllib2
>>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml')
>>> opener = urllib2.build_opener()
>>> firstdatastream = opener.open(request)
>>> firstdatastream.headers.dict                       
{'date': 'Thu, 15 Apr 2004 20:42:41 GMT',
'server': 'Apache/2.0.49 (Debian GNU/Linux)',
'content-type': 'application/atom+xml',
'last-modified': 'Thu, 15 Apr 2004 19:45:21 GMT',
'etag': '"e842a-3e53-55d97640"',
'content-length': '15955',
'accept-ranges': 'bytes',
'connection': 'close'}
>>> request.add_header('If-Modified-Since',
...     firstdatastream.headers.get('Last-Modified'))  
>>> seconddatastream = opener.open(request)            
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "c:\python23\lib\urllib2.py", line 326, in open
    '_open', req)
  File "c:\python23\lib\urllib2.py", line 306, in _call_chain
    result = func(*args)
  File "c:\python23\lib\urllib2.py", line 901, in http_open
    return self.do_open(httplib.HTTP, req)
  File "c:\python23\lib\urllib2.py", line 895, in do_open
    return self.parent.error('http', req, fp, code, msg, hdrs)
  File "c:\python23\lib\urllib2.py", line 352, in error
    return self._call_chain(*args)
  File "c:\python23\lib\urllib2.py", line 306, in _call_chain
    result = func(*args)
  File "c:\python23\lib\urllib2.py", line 412, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 304: Not Modified
  还记得当调试标记设置为开启时所有那些你看到的 HTTP 头信息打印输出吗? 这里便是你如何以编程方式访问它们: firstdatastream.headers 是 一个类似 dictionary 行为的对象 并且 允许你获得任何个别的从 HTTP 服务器返回的头信息。
  在第二次请求时, 你用第一次请求获得的最近修改时间添加了 If-Modified-Since 头信息。 如果数据没被改变, 服务器应该返回一个 304 状态代码。
  毫无疑问, 数据没被改变。 你可以从跟踪返回结果看到 urllib2 扔掉了特殊异常, HTTPError, 响应中的 304 状态代码。 这有点不寻常, 并且完全没有任何帮助。 毕竟, 它不是个错误; 你明确地询问服务器如果没有变化就不要发送任何数据, 并且数据没有变化, 所以服务器告诉你它没有为你发送任何数据。 那不是个错误; 实际上也正是你所期望的。

urllib2 也为你认为是错误的其他条件引发 HTTPError 异常, 比如 404 (page not found)。 实际上, 它将为 任何 除了状态代码 200 (OK), 301 (permanent redirect), 或 302 (temporary redirect) 之外的状态引发 HTTPError。 当你企图捕获状态代码并简单返回它, 不抛弃任何异常时, 这应该对你很有帮助。 为了实现它, 你将需要自定义一个 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
219#
 楼主| 发表于 2006-7-18 23:47 | 只看该作者
例 11.7. 定义 URL 头信息
这个自定义的 URL 头信息是 openanything.py 的一部分。


class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler):   
    def http_error_default(self, req, fp, code, msg, headers):
        result = urllib2.HTTPError(                           
            req.get_full_url(), code, msg, headers, fp)      
        result.status = code                                   
        return result                                         
  urllib2 是围绕 URL 头信息而设计的。 每一个头信息就是一个能定义任意数量方法的类。 当某事件发生 -- 比如一个 HTTP 错误, 以至一个 304 代码 -- urllib2 审视用于处理它的 一系列已定义的处理器方法。你使用了一个与 第 9 章 XML 处理 类似的自省为不同节点类型定义了一些处理器,但是 urllib2 是很灵活的,还可以内省为当前请求所定义的所有处理器。  
  当从服务器遇到一个 304 状态代码, urllib2 查找定义的操作并调用 http_error_default 方法。 通过定义一个自定义的错误处理, 你可以阻止 urllib2 引发异常。 取而代之的是, 你创建 HTTPError 对象, 返回它而不是引发异常。
  这是关键部分: 返回之前, 你保存从 HTTP 服务器返回的状态代码。 这将使你从调用程序轻而易举地访问它。

例 11.8. 使用自定义 URL 头信息
>>> request.headers                           
{'If-modified-since': 'Thu, 15 Apr 2004 19:45:21 GMT'}
>>> import openanything
>>> opener = urllib2.build_opener(
...     openanything.DefaultErrorHandler())   
>>> seconddatastream = opener.open(request)
>>> seconddatastream.status                  
304
>>> seconddatastream.read()                  
''
  继续前面的例子, Request 对象已经被设置, 并且你已经添加了 If-Modified-Since 头信息。
  [todo]这是关键所在: 既然已经定义了你的自定义 URL 头信息, 你需要告诉 urllib2 来使用它。 还记得我怎么说 urllib2 将一个 HTTP 资源的访问过程分解为三个步骤的正当理由吗?这便是为什么构建 HTTP 开启器就是它自身的步骤,因为你能用你自定义的 URL 操作覆盖 urllib2 的默认行为来创建它。
  现在你可以快速地打开一个资源, 返回给你的是, 连同常规头信息在内的对象 (使用 seconddatastream.headers.dict 访问他们), 也包括 HTTP 状态代码。 在这种情况下, 向你所期望的, 状态代码是 304, 意谓着此数据自从上次请求后没有被修改。
  注意当服务器返回 304 状态代码时, 并没有重新发送数据。 这就是全部的关键: 没有重新下载未修改的数据节省了带宽。 因此若你确实想要那个数据, 你需要在首次获得它时在本地缓存数据。

处理 ETag 的工作也非常相像, 不是检查 Last-Modified 并发送 If-Modified-Since, 而是检查 ETag 并发送 If-None-Match。 让我们打开一个新的 IDE 会话。

使用道具 举报

回复
论坛徽章:
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
220#
 楼主| 发表于 2006-7-18 23:47 | 只看该作者
例 11.9. Supporting ETag/If-None-Match
>>> import urllib2, openanything
>>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml')
>>> opener = urllib2.build_opener(
...     openanything.DefaultErrorHandler())
>>> firstdatastream = opener.open(request)
>>> firstdatastream.headers.get('ETag')        
'"e842a-3e53-55d97640"'
>>> firstdata = firstdatastream.read()
>>> print firstdata                           
<?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 -->
>>> request.add_header('If-None-Match',
...     firstdatastream.headers.get('ETag'))   
>>> seconddatastream = opener.open(request)
>>> seconddatastream.status                    
304
>>> seconddatastream.read()                    
''
  使用 firstdatastream.headers 伪字典, 你可以获得从服务器返回的 ETag (如果服务器没有返回 ETag 会发生什么? 那么这一行将返回 None.)
  OK, 你获得了数据。
  现在进行第二次调用,将 If-None-Match 头信息设置为你第一次调用获得的 ETag。  
  第二次调用静静地成功了 (没有出现任何的异常), 并且你有一次看到了从服务器返回的 304 状态代码。 你第二次基于 ETag 发送请求, 服务器知道数据没有被改变。
  无论 304 是否是被 Last-Modified 数据检查或 ETag hash 匹配触发的, 你从来都不会连同数据一起获得 304。 这就是重点所在。


在这些例子中, HTTP 服务器同时支持 Last-Modified 和 ETag 头信息, 但并非所有的服务器皆如此。 作为一个 web 服务的客户, 你应该为支持两种头信息做准备, 但是你的程序也应该为服务器仅支持其中一种头信息或两种头信息都不支持而做准备。

使用道具 举报

回复

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

本版积分规则 发表回复

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