楼主: 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
201#
 楼主| 发表于 2006-7-18 23:41 | 只看该作者
10.5. 通过节点类型创建独立的处理句柄 Creating separate handlers by node type
第三个有用的 XML 处理技巧是将你的代码基于节点类型和元素名称分散到逻辑函数中。解析后的 XML 文档是由各种类型的节点组成的,每一个都是通过 Python 对象表示的。文档本身的根层次通过一个Document对象表示。Document还包含了一个或者多个Element对象(for actual XML tags),其中的每一个可以包含其它的Element对象,Text对象(for bits of text),或者Comment对象(for embedded comments)。 Python 使编写分离每个节点类型逻辑的分发器非常容易。

例 10.17. 已解析 XML 对象的类名
>>> from xml.dom import minidom
>>> xmldoc = minidom.parse('kant.xml')
>>> xmldoc
<xml.dom.minidom.Document instance at 0x01359DE8>
>>> xmldoc.__class__                  
<class xml.dom.minidom.Document at 0x01105D40>
>>> xmldoc.__class__.__name__         
'Document'  暂时假设kant.xml在当前目录中。
  正如你在第 9.2 节 “包”中看到的,解析 XML 文档返回的对象是一个Document对象,就像在xml.dom包的minidom.py中定义的一样。又如你在第 5.4 节 “类的实例化”中看到的,__class__是每个 Python 对象的一个内置属性。
  此外,__name__是每个 Python 类的内置属性,是一个字符串。这个字符串并不神秘;它和你在定义类时输入的类名相同。(参见第 5.3 节 “类的定义”。)

好,现在你能够得到任何特定 XML 节点的类名了(因为每个 XML 节点都是以一个 Python 对象表示的)。你怎样才能利用这点来分离解析每个节点类型的逻辑呢?答案就是 getattr,你第一次见它是在第 4.4 节 “通过 getattr 获取对象引用”中。

例 10.18. parse, 一个通用的 XML 节点分发器
    def parse(self, node):         
        parseMethod = getattr(self, "parse_%s" % node.__class__.__name__)  
        parseMethod(node)   First off, 注意你正在基于传入节点(在node参数中)的类名构造一个较大的字符串。所以如果你传入一个Document节点,你就构造了字符串'parse_Document',其它类同于此。
  现在你可以把这个字符串当作一个函数名称,然后通过 getattr 得到函数自身的引用。
  最后,你可以调用函数并将节点自身作为参数传入。下一个例子将展示每个函数的定义。

使用道具 举报

回复
论坛徽章:
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
202#
 楼主| 发表于 2006-7-18 23:41 | 只看该作者
例 10.19. parse分发者调用的函数
    def parse_Document(self, node):
        self.parse(node.documentElement)

    def parse_Text(self, node):   
        text = node.data
        if self.capitalizeNextWord:
            self.pieces.append(text[0].upper())
            self.pieces.append(text[1:])
            self.capitalizeNextWord = 0
        else:
            self.pieces.append(text)

    def parse_Comment(self, node):
        pass

    def parse_Element(self, node):
        handlerMethod = getattr(self, "do_%s" % node.tagName)
        handlerMethod(node)  parse_Document只会被调用一次,因为在一个 XML 文档中只有一个Document节点,并且在已解析 XML 的表示中只有一个Document对象。它只是turn around并解析语法文件的根元素。
  parse_Text 在节点表示文本时被调用。这个函数本身做某种特殊处理,自动将句子的第一个单词进行大写处理,而不是简单的将表示的文本追加到一个列表中。
  parse_Comment 只有一个pass,因为你并不关心语法文件中嵌入的注释。但是注意,你还是要定义这个函数并显式的让它不做任何事情。如果这个函数不存在,通用parse函数在遇到一个注释的时候,会执行失败,因为它试图找到并不存在的parse_Comment函数。为每个节点类型定义独立的函数,甚至你不要使用的,将会使通用parse函数保持简单和沉默。
  parse_Element方法其实本身就是一个分发器,它基于元素的标记名称。这个基本概念是相同的:使用元素的区别(它们的标记名称)然后针对每一个分发到一个独立的函数。你构建了一个类似于'do_xref'的字符串(对<xref>标记而言),找到这个名称的函数,并调用它。对其它的标记名称在解析语法文件的时候都可以找到类似的函数(<p>标记,<choice>标记)。

在这个例子中,分发函数parse和parse_Element只是找到相同类中的其它方法。如果你进行的处理过程很复杂(或者你有很多不同的标记名称),你可以将代码分散到独立的模块中,然后使用动态导入的方式导入每个模块并调用你需要的任何函数。动态导入将在第 16 章 有效编程(Functional Programming)中介绍。

使用道具 举报

回复
论坛徽章:
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
203#
 楼主| 发表于 2006-7-18 23:41 | 只看该作者
10.6. 处理命令行参数
Python 完备支持创建在命令行运行的程序,并且连同命令行参数和短长样式来指定各种选项。这些并非是 XML 特定的,但是这样的脚本可以充分使用命令行处理,看来是时候提一下它了。

如果不理解命令行参数如何暴露给你的 Python 程序,讨论命令行处理是很困难的,所以让我们先写个简单那的程序来看一下。

例 10.20. sys.argv 介绍
如果您还没有下载本书附带的例子程序, 可以 下载本程序和其他例子程序。

#argecho.py
import sys

for arg in sys.argv:
    print arg  每个传递给程序的命令行参数都在sys.argv,它仅仅是一个列表。这里是在独立行中打印出每个参数。

例 10.21. sys.argv的内容
[you@localhost py]$ python argecho.py            
argecho.py
[you@localhost py]$ python argecho.py abc def     
argecho.py
abc
def
[you@localhost py]$ python argecho.py --help      
argecho.py
--help
[you@localhost py]$ python argecho.py -m kant.xml
argecho.py
-m
kant.xml  关于sys.argv需要了解的第一件事情是它包含了你正在调用的脚本的名称。你后面会实际使用这个知识,在第 16 章 有效编程(Functional Programming)中。现在不用担心
  命令行参数通过空格进行分隔,在sys.argv类表中,每个参数都是一个独立的元素。
  命令行标志,就像--help,在sys.argv列表中还保存了它们自己的元素。
  为了让事情更有趣,有些命令行标志本身就接收参数。比如,这里有一个标记(-m)接收一个参数(kant.xml)。标记自身和标记参数只是sys.argv列表中的序列元素。并没有试图将元素与其它元素进行关联;所有你得到的是一个列表。

所以正如你所看到的,你确实拥有了命令行传入的所有信息,但是, but then again, it doesn't look like it's going to be all that easy to actually use it. 对于只是接收单个参数或者没有标记的简单程序,你可以简单的使用sys.argv[1]来访问参数。这没有什么羞耻的;我一直都是这样做的。对更复杂的程序,你需要 getopt 模块。

使用道具 举报

回复
论坛徽章:
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
204#
 楼主| 发表于 2006-7-18 23:41 | 只看该作者
例 10.22. getopt 介绍

def main(argv):                        
    grammar = "kant.xml"                 
    try:                                
        opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="])
    except getopt.GetoptError:           
        usage()                          
        sys.exit(2)                     

...

if __name__ == "__main__":
    main(sys.argv[1:])  First off,看一下例子最后并注意你正在调用main函数,参数是sys.argv[1:]。记住,sys.argv[0]是你正在运行脚本的名称;对命令行而言,你不用关心它,所以你可以砍掉它并传入列表的剩余部分。
  这里就是所有有趣处理发生的地方。getopt 模块的getopt 函数接收三个参数:参数列表(你从sys.argv[1:]得到的),一个包含了程序所有可能接收到的单字符命令行标志,和一个等价于单字符的长命令行标志的列表。第一次看的时候,这有点混乱,下面有更多的细节解释。
  在解析这些命令行标志时,如果有任何事情错了,getopt 会抛出异常,你可以捕获它。你可以告诉 getopt 你明白的所有标志,那么这也意味着终端用户可以传入一些你不理解的命令行标志。
  和 UNIX 世界中的标准实践一样,如果脚本被传入了不能理解的标志,你要打印出正确用法的一个概要并友好的退出。注意,在这里我没有写出usage函数。你还是要在某个地方写一个,使它打印出合适的概要;它不是自动的。

那么你传给 getopt 函数的参数是什么呢?好的,第一个单单只是一个命令行标志和参数的原始列表(不包括第一个元素,脚本名称,你在调用main函数之前就已经将它砍掉了)。第二个是脚本接收的短命令行标志的一个列表。

"hg:d"
-h
print usage summary
-g ...
use specified grammar file or URL
-d
show debugging information while parsing
第一个标志和第三个标志是简单的独立标志;你选择是否指定它们,它们做某些事情(打印帮助)或者改变状态(关闭调试)。但是,第二个标志(-g)必须跟随一个参数,进行读取的语法文件的名称。实际上,它可以是一个文件名或者一个web地址,你可能还不知道(后面你会明白的),但是你要知道必须要有些东西。所以,你可以通过在 getopt 函数的第二个参数的g后面放一个冒号,来向 getopt 说明这一点。

To further complicate things,这个脚本接收短标志(像-h)或者长标记(像--help),并且你要它们做相同的事。这就是 getopt 第三个参数存在的原因,为了指定长标志的一个列表,其中的长标志是和第二个参数中指定的短标志相对应的。

["help", "grammar="]
--help
print usage summary
--grammar ...
use specified grammar file or URL
这里要注意的三件事:

所有命令行中的长标志以两个短划线开始,但是在调用 getopt 时,你不用包含这两个短划线。它们是能够被理解的。
--grammar标志的后面必须跟着另一个参数,就像-g标志一样。通过等于号标识出来 "grammar="。
长标志列表比短标志列表更短一些,因为-d标志没有相应的长标志。这也好;只有-d才会打开调试。但是短标志和长标志的顺序必须是相同的,你应该先指定有长标志的短标志,然后才是剩下的短标志。
被搞昏没?让我们看一下真实的代码,看看它在上下文中是否起作用。

例 10.23. 在 kgp.py 中处理命令行参数

def main(argv):                          
    grammar = "kant.xml"               
    try:                                
        opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="])
    except getopt.GetoptError:         
        usage()                        
        sys.exit(2)                     
    for opt, arg in opts:               
        if opt in ("-h", "--help":      
            usage()                     
            sys.exit()                  
        elif opt == '-d':               
            global _debug               
            _debug = 1                  
        elif opt in ("-g", "--grammar":
            grammar = arg               

    source = "".join(args)               

    k = KantGenerator(grammar, source)
    print k.output()  grammar变量会跟踪你正在使用的语法文件。如果你没有在命令行指定它(使用-g或者--grammar标志定义它),在这里你将初始化它。
  你从 getopt 取回的opts变量包含了元组(flag 和 argument)的一个列表。如果标志没有带任何参数,那么arg只是 None 。这使得遍历标志更容易了。
  getopt 验证命令行标志是否可接受,但是它不会在短标志和长标志之间做任何转换。如果你指定-h标志,opt将会包含"-h";如果你指定--help标志,opt将会包含"--help"标志。所以你需要检查它们两个。
  记得,-d标记没有相应的长标志,所以你只需要检查短形式。如果你找到了它,你就可以设置一个全局变量来指示后面要打印出调试信息。(我习惯在脚本的开发过程中使用它。What, you thought all these examples worked on the first try?)
  如果你找到了一个语法文件,-g标志或者--grammar标志带着的,那你要保存跟在它(保存在arg)后面的参数到变量grammar中,覆盖掉在main函数你初始化的默认值。
  That’s it。你已经遍历并处理了所有的命令行标志。这意味着所有剩下的东西都必须是命令行参数。这些从 getopt 函数的args变量回来。在这个例子中,你把它们当作了解析器源材料。如果没有指定命令行参数,args将是一个空列表,并且source将以空字符串结束。

使用道具 举报

回复
论坛徽章:
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
205#
 楼主| 发表于 2006-7-18 23:42 | 只看该作者
10.7. 全部放在一起
你已经了解很多基础的东西。让我们回来看看所有片段是如何整合到一起的。

作为开始,这里是一个接收命令行参数的脚本,它使用 getopt 模块。


def main(argv):                        
...
    try:                                
        opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="])
    except getopt.GetoptError:         
...
    for opt, arg in opts:               
...创建KantGenerator类的一个实例,然后将语法文件文件和源传给它,可能在命令行没有指定。

    k = KantGenerator(grammar, source)KantGenerator实例自动加载语法,它是一个 XML 文件。你使用自定义的 openAnything 函数打开这个文件(可能保存在一个本地文件中或者一个远程服务器上),然后使用内置的minidom 解析函数将 XML 解析为一棵 Python 对象树。

    def _load(self, source):
        sock = toolbox.openAnything(source)
        xmldoc = minidom.parse(sock).documentElement
        sock.close()哦,根据这种方式,你将使用到 XML 文档结构的知识建立一个引用的小缓冲,这些引用只是 XML 文档中的元素。

    def loadGrammar(self, grammar):                        
        for ref in self.grammar.getElementsByTagName("ref":
            self.refs[ref.attributes["id"].value] = ref     如果你在命令行中指定了某些源材料,你可以使用它;否则你将打开语法查找“顶层”引用(没有被其它的东西引用)并把它作为开始点。

    def getDefaultSource(self):
        xrefs = {}
        for xref in self.grammar.getElementsByTagName("xref":
            xrefs[xref.attributes["id"].value] = 1
        xrefs = xrefs.keys()
        standaloneXrefs = [e for e in self.refs.keys() if e not in xrefs]
        return '<xref id="%s"/>' % random.choice(standaloneXrefs)现在你打开了了源材料。它是一个 XML 你每次解析一个节点。为了让代码分离并具备更高的可维护性,你可以使用针对每个节点类型的独立处理方法。

    def parse_Element(self, node):
        handlerMethod = getattr(self, "do_%s" % node.tagName)
        handlerMethod(node)通过语法的反弹,解析所有 p 元素的孩子,

    def do_p(self, node):
...
        if doit:
            for child in node.childNodes: self.parse(child)用任意一个孩子替换 choice 元素,

    def do_choice(self, node):
        self.parse(self.randomChildElement(node))并用对应 ref 元素的任意孩子替换 xref ,前面你已经进行了缓冲。

    def do_xref(self, node):
        id = node.attributes["id"].value
        self.parse(self.randomChildElement(self.refs[id]))最后,你以你的方式进行解析直到普通文本。

    def parse_Text(self, node):   
        text = node.data
...
            self.pieces.append(text)你打印出来的。


def main(argv):                        
...
    k = KantGenerator(grammar, source)
    print k.output()

使用道具 举报

回复
论坛徽章:
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
206#
 楼主| 发表于 2006-7-18 23:42 | 只看该作者
10.8. 小结
Python 带有解析和操作 XML 文档非常强大的库。这个 minidom 接收一个 XML 文件并将其解析为 Python 对象,提供了对任意元素的随即访问。进一步,本章展示了如何利用 Python 创建一个“真实”独立的命令行脚本,连同命令行标志,命令行参数,错误处理,甚至从前一个程序的管道接收输入的能力。

在继续下一章前,你应该无困难的完成所有这些事情:

通过标准输入输出链接程序
使用 getattr 定义动态分发器。
通过 getopt 使用命令行标志并进行验证

使用道具 举报

回复
论坛徽章:
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
207#
 楼主| 发表于 2006-7-18 23:44 | 只看该作者
第 11 章 HTTP Web 服务
11.1. 概览
11.2. 避免通过 HTTP 重复地获取数据
11.3. HTTP 的特性
11.3.1. 用户代理 (User-Agent)
11.3.2. 重定向 (Redirects)
11.3.3. Last-Modified/If-Modified-Since
11.3.4. ETag/If-None-Match
11.3.5. 压缩 (Compression)
11.4. 调试 HTTP web 服务
11.5. 设置 User-Agent
11.6. 处理 Last-Modified 和 ETag
11.7. 处理重定向
11.8. 处理被压缩的数据
11.9. 全部放在一起
11.10. 小结
11.1. 概览
在讲解 如何下载 web 页 和 如何从 URL 解析 XML时, 你已经学习了关于 HTML 处理 和 XML 处理, 接下来让我们来更全面地探讨有关 HTTP web 服务的主题。

简单地讲, HTTP web 服务是指直接使用 HTTP 操作从远程服务器按部就班地发送和接收数据。如果你要从服务器获取数据, 直接使用 HTTP GET; 如果您想发送新数据到服务器, 使用 HTTP POST。(一些较高级的 HTTP web 服务 API 也定义了使用 HTTP PUT 和 HTTP DELETE 修改和删除现有数据的方法。) 换句话说, 构建在 HTTP 协议中的 “verbs(动作)” (GET, POST, PUT 和 DELETE) 直接映射为接收, 发送, 修改和删除等应用级别的操作。

利用这种方法的要点是简单的,并且许多不同的站点充分印证了这样的简单性是受欢迎的。数据(通常是 XML 数据 ) 能静态创建和存储, 或通过服务器端脚本和所有主流计算机语言(包括用于下载数据的 HTTP 库)动态生成。 调试也很简单, 因为您可以在任意浏览器中调用网络服务来查看这些原始数据。 现代浏览器甚至可以为您进行良好地格式化并漂亮地打印这些 XML 数据, 以便让您快速地浏览。

HTTP web 服务上的纯 XML 应用举例:

在后面的几章里, 我们将探索使用 HTTP 做数据发送和接收传输的 API, 但是不会将应用语义映射到潜在的 HTTP 语义。 (所有这些都是通过 HTTP POST 这个管道完成的。) 但是本章将关注使用 HTTP GET 从远程服务器获取数据, 并且将探索几个由纯 HTTP web 服务带来最大利益的 HTTP 特性。
如下所示为 上一章 曾经看到过的 openanything 模块的更高级版本 :
例 11.1. openanything.py
如果您还没有下载本书附带的例子程序, 可以 下载本程序和其他例子程序
import urllib2, urlparse, gzip
from StringIO import StringIO
USER_AGENT = 'OpenAnything/1.0 +http://diveintopython.org/http_web_services/'
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                                       
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                                         
def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
    '''URL, filename, or string --> stream
    This function lets you define parsers that take any input source
    (URL, pathname to local or network file, or actual data as a string)
    and deal with it in a uniform manner.  Returned object is guaranteed
    to have all the basic stdio read methods (read, readline, readlines).
    Just .close() the object when you're done with it.
    If the etag argument is supplied, it will be used as the value of an
    If-None-Match request header.
    If the lastmodified argument is supplied, it must be a formatted
    date/time string in GMT (as returned in the Last-Modified header of
    a previous request).  The formatted date/time will be used
    as the value of an If-Modified-Since request header.
    If the agent argument is supplied, it will be used as the value of a
    User-Agent request header.
    '''
    if hasattr(source, 'read'):
        return source
    if source == '-':
        return sys.stdin
    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)                                                
   
    # try to open with native open function (if source is a filename)
    try:
        return open(source)
    except (IOError, OSError):
        pass
    # treat source as string
    return StringIO(str(source))
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

使用道具 举报

回复
论坛徽章:
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
208#
 楼主| 发表于 2006-7-18 23:44 | 只看该作者
进一步阅读

使用道具 举报

回复
论坛徽章:
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
209#
 楼主| 发表于 2006-7-18 23:45 | 只看该作者
11.2. 避免通过 HTTP 重复地获取数据
假如说你想用 HTTP 下载资源, 例如一个 Atom feed 汇聚。你不仅仅想下载一次; 而是想一次又一次地下载它, 如每小时一次, 从提供 news feed 的站点获得最新的消息。让我们首先用一种直接而原始的方法来实现它, 然后看看如何改进它。

例 11.2. 用直接而原始的方法下载 feed
>>> import urllib
>>> data = urllib.urlopen('http://diveintomark.org/xml/atom.xml').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 -->
  使用Python 通过 HTTP 下载任何东西都简单得令人难以置信; 实际上, 只需要一行代码。 urllib 模块有一个便利的 urlopen 函数,它接受您所要获取的页面地址, 然后返回一个类似文件的对象,您仅仅使用 read() 便可获得页面的全部内容。 这再简单不过了。  

那么这种方法有何不妥之处吗? 当然, 在测试或开发中一次性的使用没有什么不妥。我经常这样。我想要 feed 汇聚的内容, 我就获取 feed 的内容。 这种方法对其他 web 页面同样有效。 但是一旦你开始按照 web 服务的方式去思考有规则的访问需求时(记住, 你说你计划每小时一次地重复获取这样的 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
210#
 楼主| 发表于 2006-7-18 23:45 | 只看该作者
11.3. HTTP 的特性
11.3.1. 用户代理 (User-Agent)
11.3.2. 重定向 (Redirects)
11.3.3. Last-Modified/If-Modified-Since
11.3.4. ETag/If-None-Match
11.3.5. 压缩 (Compression)
这里有五个你必须关注的 HTTP 重要特性。

11.3.1. 用户代理 (User-Agent)
User-Agent 是一种客户端告知服务器谁在什么时候通过 HTTP 请求了一个 web 页, feed 汇聚或其他类型的 web 服务的简单途径。 当客户请求一个资源时, 应该尽可能明确发起请求的是谁。 以便当产生异常错误时,允许服务器端的管理员与客户端的开发者取得联系。

默认情况下 Python 发送一个通用的 User-Agent: Python-urllib/1.15。 下一节, 您将看到更加有针对性的 User-Agent。

使用道具 举报

回复

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

本版积分规则 发表回复

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