之前提到过XML,现在该更详细的讨论它了。在这个项目中,你将看到XML可用来表示各种类型的数据,以及如何使用Simple API for XML(SAX)来处理XML文件。这个项目的目标是,根据描述各种网页和目录的单个XML文件生成完整的网站。
现在假设你知道XML是什么以及如何编写。如果你对HTML有些了解,就已经熟悉了这些基本知识。不像HTML那样是一种特定的语言,XML是一组定义一类语言的规则。大致而言,你依然可以像使用HTML那样编写标签,但在XML中,还可以自定义标签名。这些标签名及其结构关系可使用文档类型定义(document type definition)或XML架构(XML Schema)来描述,但这里不讨论这些。
有关XML的简洁描述,请参阅万维网联盟(W3C)网站的文章“XML in 10 points”(http://www.w3.org/XML/1999/XML-in-10-points-19990327)。有关XML的详尽教程,请参阅W3Schools网站(http://www.w3schools.com/xml)。有关SAX的详细信息,请参阅SAX官网(http://www.saxproject.org)。
1.问题描述
在这个项目中,要解决的通用问题是解析(读取并处理)XML文件。鉴于XML几乎可用来表示任何信息,而你可对其中的数据做任何处理,因此正如标题指出的,今天介绍的技巧拥有非常广泛的用途。今天要解决的具体问题是,根据一个XML文件生成完整的网站,而这个文件描述了网站的结构以及每个网页的基本内容。
着手处理这个项目前,建议你花点时间了解XML及其用途。这样你可能有更深入的认识,知道在什么情况下使用这种格式很有用,什么情况下使用它犹如大炮打蚊子。(毕竟,有时候用纯文本文件足够了)。
XML可用来表示任何信息
你可能对此持怀疑态度,下面来看几个有关其用途的示例。
XML Cover Pages(http://xml.coverpages.org/xml.html#applications)提供了一些现有的XML应用示例。
下面来确定这个项目的具体目标。
仅考虑到最后一点,就职的创建这样的XML文件了,但还有其他的好处。通过将所有的内容放在一个XML文件中,可轻松地编写其他程序,以使用同样的XML处理技术来提取各种信息,如目录和供自定义搜索引擎使用的索引等。另外,就算不用来创建网站,也可使用这种文件来创建基于HTML的幻灯片或PDF幻灯片(方法是使用之前讨论的ReportLab)。
2.有用的工具
Python本身提供了对XML的支持,但如果你使用的版本过旧,可能需要安装额外的模块。在这个项目中,需要一个管用的SAX解析器。要确定是否已经有这样的SAX解析器,可尝试执行如下代码:
当你这样做时,很可能不会发生异常。如果是这样,就说明万事俱备,可以接着阅读下一节了。
提示 有很多Python的XML工具,除标准框架PyXML外,另一个很有趣的工具是Fredrik Lundh开发的ElementTree(及其C语言实现的cElementTree)。在较新的Python版本中,标准库包含这个工具,它位于xml.etree包中。如果你使用的Python版本较旧,可从http://effbot.org/zone获取ElementTree。这个工具功能强大却易于使用,如果你很重视Python处理XML,就值得花时间去研究它。
如果出现异常,就必须安装PyXML。只要在网上搜索一下,就应该能够找到安装指南(但除非你的Python版本很古老,否则应提供了XML支持)。
3.准备工作
要编写处理XML文件的程序,必须先设计要使用的XML格式。需要哪些标签?这些标签应包含哪些属性?各个标签都用来做什么?为了回答这些问题,首先需要考虑你使用这种XML格式来描述什么。
主要的概念包括网站、目录、页面、名称、标题和内容。
总之,XML文档只包含一个website元素,这些元素包含多个directory和page元素,其中每个directory元素都可能包含page和directory元素。directory和page都包含属性name,而该属性包含目录和页面的名称。另外,page元素还有属性title。page元素包含XHTML代码(这种代码类型是在XHTML body标签中指定的)。下图是一个这样的示例文件。
4.初次实现
到目前为止,还没有介绍XML解析的工作原理。这里使用的方法名为SAX,他要求我们编写一系列事件处理程序(与GUI编程中一样),并让XML解析器在读取XML文档时调用这些处理程序。
使用DOM如何
在Python(和其他编程语言)中,处理XML最常见的方式有两种:SAX和文档对象模式(DOM)。SAX解析器读取XML并指出发现的内容(文本、标签和属性),但每次只存储文档的一小部分。这让SAX简单、快捷且占用内存较少,也就是我在项目中选择使用它的原因所在。DOM采用的是另一种方法:创建一个表示整个文档的数据结构(文档树)。这种方法速度更慢,需要的内存更多,但在需要操作文档的结构时很有用。
4.1.创建简单的内容处理程序
使用SAX进行解析时,可供使用的事件很多,但这里只使用其中的三个:元素开始(遇到起始标签),元素结束(遇到结束标签)和普通文本(字符)。为解析XML文件,我们将使用模块xml.sax中的函数parse。这个函数负责读取文件并生成事件,但生成事件时,它需要调用一些事件处理程序。这些事件处理程序将实现为内容处理程序对象的方法。你将从xml.sax.handler中的ContentHandler类派生出一个子类,因为这个类实现了所有必要的事件处理程序(什么都不做的伪操作),而你只需要重写需要的事件处理程序。
下面首先来创建一个极简的XML解析器(这里假设要解析的XML文件名为website.xml)。
如果执行这个程序,将看起来什么都没有发生,但也不会出现任何错误信息。然而,在幕后对这个XML文件进行了解析,但由于调用的是什么都不做的默认事件处理程序,因此没有任何输出。
下面来尝试进行简单的扩展。为此,在TestHandler类中添加如下方法:
这重写了默认事件处理程序startElement,其中的参数为相关标签的名称和属性(这些属性存储在一个类似于字典的对象中)。如果你再次运行这个程序(对website.xml进行解析),将看到如下输出:
其中的工作原理应该非常清晰。除startElement外,我们还将使用事件处理程序endElement(它只将标签名作为参数)和characters(它将一个字符串作为参数)。
下面的示例使用这三个事件处理程序来创建一个列表,其中包含网站描述文件中的所有标题(h1元素):
请注意,HeadlineHandler跟踪当前解析的文本是否位于一对h1标签内,其实现如下:在startElement发现标签为h1时将self.in_headline设置为True,并在endElement发现标签为h1时将self.in_headline设置为False。方法characters在解析器遇到文本时自动被调用。只要当前位于两个h1标签之间(self.in_headline为True),characters就将传递给它的字符串(可能只是这两个标签之间的文本的一部分)附加到字符串列表self.data的末尾。将这些文本片段合并为单个字符串,将结果附加到self.headlines末尾并将self.data重置为空列表的任务也是由endElement完成的。在SAX编程中,这种做法(使用布尔变量来指出当前是否在特定标签类型内)很常见。
现在,如果运行这个程序(仍然是对文件website.xml进行解析),将得到如下输出:
4.2.创建HTML页面
现在就可以创建原型了。我们暂时不考虑目录,而是专注于创建HTML页面。你需要稍微修改事件处理程序,使其执行如下任务。
这些任务大都非常容易理解(至少在你对HTML文档的组织结构有所了解时如此)。然而,有两个问题可能不那么显而易见。
这个简单的程序的代码如图所示。
要将文件存储到哪个目录,就应该在哪个目录执行这个脚本。请注意,即便两个页面位于不同的directory元素中,它们最终也存储到同一个目录中。(再次实现将修复这种问题。)
同样,对文件website.xml进行解析。这将得到4个HTML文件,其中的index.html包含如下内容:
下图显示了在浏览器中查看这个页面的结果。
从上述代码可知,它有两个显而易见的主要缺点。
这两个缺点在再次实现中都将得到解决。
本文分享自 Python机器学习算法说书人 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!