《Python基础教程(第2版)》学习笔记(三):

来源:互联网 发布:魔兽之路隐藏英雄数据 编辑:程序博客网 时间:2024/05/17 06:36

Proj3万能的XML

这个项目介绍了如何用XML来表示多种数据,以及如何使用适合XML或SAX的简单API来处理XML文件。XML并不是一种具体的语言(例如HTML),它更类似于定义语言的一种规则集合。我们仍然使用HTML中的方式来写标签,但在XML中,还可以自己定义标签名。

工作和目标

本项目解决的问题就是解析(读取和处理)XML文件,具体来讲是通过一个描述各种网页和目录的XML文件生成一个完整的网站。这个文件包括站点结构和每个页面的基本内容。
目标列举如下:
-整个网站应该用一个XML文件描述,其中包括独立网页和目录的信息。
-程序应该能创建所需的目录和网页
-应该可以轻松改变整个网站的设计,并且以新的设计为基础重新生成所有网页。

工具

Python有一些内建的XML支持,查看是否已有一个可以用的SAX语法分析器,执行下面语句:

from xml.sax import parseparser = make_parser()

执行这条命令不引发任何异常即可以开始项目了。

准备工作

编写处理XML文件的程序前,需要设计XML格式。主要概念包括网站、目录、页面、名称、标题和内容。
-网站。不用存储有关网站本身的任何信息,所以网站就是包括所有文件和目录的顶级元素。
-目录。目录是文件和其他目录的容器。
-页面。一个网页。
-名称。目录和网页都需要名称–当目录和文件出现在文件系统和相应的URL中时,它们可以用作目录名和文件名。
-标题。每个网页都应该有标题(和文件名不同)。
-内容。每个网页都有一些内容。
简单来说,文档由一个包含directory和page元素的website元素组成,每个目录元素可以包括更多的页面和目录。directory和page元素有叫做name的特性,属性值是它们的名字。除此之外,page标签还有title特性。page元素包括XHTML代码。一个实例文件是Proj3万能的XML中的website.xml。如下:

<website>    <page name="index" title="Home Page">        <h1>Welcome to My Home Page</h1>        <p>Hi,there.My name is Alexis,and this is my home page.Here        are some of my interests:</p>        <ul>            <li><a href="interests/shouting.html">Shouting</a></li>            <li><a href="interests/sleeping.html">Sleeping</a></li>            <li><a href="interests/eating.html">Eating</a></li>        </ul>    </page>    <directory name="interests">        <page name="shouting" title="Shouting">            <h1>Alexis's Shouting Page</h1>            <p>...</p>        </page>        <page name="sleeping" title="Sleeping">            <h1>Alexis's Sleeping Page</h1>            <p>...</p>        </page>        <page name="eating" title="Eating">            <h1>Alexis's Eating Page</h1>            <p>...</p>        </page>    </directory></website>

初次实现

创建简单的内容处理程序

在使用SAX进行解析时,有很多种事件类型可用,这里只用到3个:startElement(开始标签的匹配项)、endElement(关闭标签的匹配项)以及characters(字符)。使用xml.sax模块中的parse函数负责读取并且生成这些事件。要生成这些事件需要调用一些处理程序(content handler),xml.sax.handler中的ContentHandler类实现了这些处理程序,但函数具体工作需要用户重写。解析的具体原理可以参看这个链接(http://www.cnblogs.com/hongfei/p/python-xml-sax.html)。假如要解析一个<h1>级别的标题,可以像下面这样编写一个类:

from xml.sax.handler import ContentHandlerfrom xml.sax import parseclass HeadlineHandler(ContentHandler):    in_headline = False    def __init__(self,headlines):        ContentHandler.__init__(self)        self.headlines = headlines        self.data = []    def startElement(self,name,attrs):        if name == 'h1':            self.in_headline = True    def endElement(self,name):        if name == 'h1':            text = ''.join(self.data)            self.data = []            self.headlines.append(text)            self.in_headline = False    def characters(self,string):            if self.in_headline:            self.data.append(string)

这种解析类都至少包括上面说的三种事件类型的处理程序。具体执行时可以像这样调用:

headlines = []parse('website.xml',HeadlineHandler(headlines))print 'The following <h1> elements were found:'for h in headlines:    print h

打印出的结果如下图:
解析<code><h1></code>结果
简单的过程就是当找到<h1>标签时,调用startElement,开关打开。在结束标签之前都会调用characters收集字符串。当遇到结束标签</h1>时,执行endElement函数,处理收集到的字符串,添加到存储题目的列表中。

创建HTML页面

像上一节的例子处理<h1>标签那样那样,我们需要编写一个还能处理<page>等其他标签的完整的HTML页面解析程序(暂时不考虑目录的解析)。这个简单版本的代码在工程文件夹下的pagemaker.py文件中。运行结果如下所示:

<html><head><title>Home Page</title></head><body>        <h1>Welcome to My Home Page</h1>        <p>Hi,there.My name is Alexis,and this is my home page.Here        are some of my interests:</p>        <ul>            <li><a href="interests/shouting.html">Shouting</a></li>            <li><a href="interests/sleeping.html">Sleeping</a></li>            <li><a href="interests/eating.html">Eating</a></li>        </ul></body></html>

再次实现

调度程序的混入类

与其在标准的泛型事件处理程序(比如startElement)中编写大段的if语句,不如编写自己的具体程序(比如startPage),并且自动调用它们。一个提供有限功能的混入类(本文中是Dispatcher)和其他更具体的类(本文中是ContentHandler类)一起被继承,生成的新类WebsiteConstructor才是最终被parse使用的类。Dispatcher类中的方法dispatch()类似于项目1中的根据名称查找函数,该类还包括基本的startElement和endElement事件处理。

class Dispatcher:    def dispatch(self,prefix,name,attrs=None):        mname = prefix + name.capitalize()        dname = 'default' + prefix.capitalize()        method = getattr(self,mname,None)        if callable(method): args = ()        else:            method = getattr(self,dname,None)            args = name,        if prefix == 'start': args += attrs,        #args是一个元组        if callable(method): method(*args)    def startElement(self,name,attrs):        self.dispatch('start',name,attrs)    def endElement(self,name):        self.dispatch('end',name)

下面就是实现具体的处理程序了:

class WebsiteConstructor(Dispatcher,ContentHandler):    passthrough = False    def __init__(self,directory):        self.directory = [directory]        self.ensureDirectory()    def ensureDirectory(self):        path = os.path.join(*self.directory)        if not os.path.isdir(path): os.makedirs(path)    def characters(self,chars):        if self.passthrough: self.out.write(chars)    def defaultStart(self,name,attrs):        if self.passthrough:            self.out.write('<' + name)            for key,val in attrs.items():                self.out.write(' %s="%s"' % (key,val))            self.out.write('>')    def defaultEnd(self,name):        if self.passthrough:            self.out.write('</%s>' % name)    def startDirectory(self,attrs):        self.directory.append(attrs['name'])        self.ensureDirectory()    def endDirectory(self):        self.directory.pop()    def startPage(self,attrs):        filename = os.path.join(*self.directory+[attrs['name']+'.html'])        self.out = open(filename,'w')        self.writeHeader(attrs['title'])        self.passthrough = True    def endPage(self):        self.passthrough = False        self.writeFooter()        self.out.close()    def writeHeader(self,title):        self.out.write('<html><head>\n   <title>')        self.out.write(title)        self.out.write('</title>\n </head>\n  <body>\n')    def writeFooter(self):        self.out.write('\n </body>\n</html>\n')

解释一下为什么要ensureDirectory。os.makedirs(‘foo/bar/baz’)函数会在当前目录中创建一个字符串中的目录,当该目录(指完整的../foo/bar/baz)本来就存在时,会引发一个异常。为了避免出现这个异常,需要使用os.path.isdir函数,它可以检查给定的路径是否是目录(也就是目录是否存在)。另外一个函数os.path.join可以使用正确的分隔符将数个路径连接起来例如:directory = [“C”, “pic”, “18x.jpg”],执行os.path.join(*directory) 会得到”C\\pic\\18x.jpg”。完整代码见website.py文件。

总结

本项目建立了一个简单的网站,非常简单,只有文本信息。要做更复杂的还需要深入学习,以后有机会再扩展一下这个程序。

0 0