Python基础教程书籍案例:解析XML(万能的XML)【上】

这个练习项目来自《Python基础教程(第2版)》,案例原名为“万能的XML”。

XML是一种可扩展标记语言,具备以下特点(来自百度百科):

  • 可扩展标记语言是一种很像超文本标记语言的标记语言。
  • 它的设计宗旨是传输数据,而不是显示数据。
  • 它的标签没有被预定义,需要自行定义标签。
  • 它被设计为具有自我描述性。
  • 它是W3C的推荐标准。

上面所说的超文本标记语言,大家已经见过。

一般超文本标记语言就是指HTML。

HTML文件由多个标记(即通常所说的标签)组成。

这些标记通常是成对出现,即一个开始标记对应一个 结束标记。

XML和HTML非常相像,它的标记也是成对出现。

不过,和HTML不同的是,HTML中的标记名称都是固定的,而XML中的标记名称是需要自定义的。

并且,XML和HTML最大不同之处是:XML的目的是传输信息,HTML的目的是显示信息。

例如,一个描述网站结构与页面内容的XML文件。

示例代码:

'''
想要学习Python?Python学习交流群:984632579满足你的需求,资料都已经上传群文件,可以自行下载!
'''
<website>
    <page name="index" title="首页">
        <h1>欢迎访问小楼的个人网站!</h1>
        <p>您正在访问的是小楼的个人网站,这个网站包含以下内容:</p>
        <ul>
            <li>
                <a href="catalog/articles.html">文章</a>
            </li>
            <li>
                <a href="catalog/downloads.html">下载</a>
            </li>
            <li>
                <a href="catalog/documents.html">文档</a>
            </li>
        </ul>
    </page>
    <directory name="catalog">
        <page name="articles" title="文章">
            <h1>您正在访问文章列表页!</h1>
            <p>....</p>
        </page>
        <page name="downloads" title="下载">
            <h1>您正在访问资源下载页!</h1>
            <p>...</p>
        </page>
        <page name="documents" title="文档">
            <h1>您正在访问文档列表页!</h1>
            <p>....</p>
        </page>
    </directory>
</website>

在上方XML代码中,通过自定义标记<website>、<directory>以及<page>定义了网站的结构。

并且,通过一些HTML标记,描述了每个网页的内容。

那么,我们就可以通过这个XML文件传输整个网站内容的数据,并通过对XML的解析,生成网站的结构和HTML页面文件。

所以,基于XML传输信息的特点,我们能够通过XML实现很多功能。

如果想很好的掌握XML,还需要深入的学习,请大家自行查阅相关技术文档。

对XML有了一些基本的了解之后,我们来看一下如何对XML文件内容进行解析。

首先,我们先来做个小试验。

通过Python内置的xml模块就能够对XML文件进行解析。

这里需要先导入xml模块的一些功能。

示例代码:

'''
想要学习Python?Python学习交流群:984632579满足你的需求,资料都已经上传群文件,可以自行下载!
'''
from xml.sax import parse
from xml.sax.handler import ContentHandler

parse()函数具有解析功能,ContentHandler类则具有内容处理的方法。

ContentHandler类所包含的方法有很多,比较常用的就是对开始元素、内容以及结束元素进行处理的方法。

这些方法,我们需要重写才能够实现我们想要的功能。

在重写方法之前,我们先来看一下通过这个类处理内容时,我们都能获取什么样的内容。

以处理开始标记的方法startElement()为例。

我们创建一个类继承自ContentHandler类,然后重写startElement()方法。

示例代码:

class XMLHandler(ContentHandler):
    def startElement(self, name, attrs):  # 重写startElement方法
        print(name, list(zip(attrs.keys(), attrs.values())))  # 显示输出参数内容

parse('website.xml', XMLHandler())  # 调用解析函数

运行上方代码,我们能够看到以下内容:

很显然,所有开始标签的名称和属性都能够被获取到。

接下来,我们再进一步,将XML中的每个网页的一级标题(<h1>标签中的内容)提取出来。

为了避免混乱,我们可以删除刚才的代码,重新定义一个类。

示例代码:

class HeadLineHandler(ContentHandler):
    def __init__(self, headLines):
        super().__init__()  # 不写对结果也没有影响
        self.headLines = headLines  # 初始化类的变量为传入的标题列表
        self.in_headLine = False  # 初始化开关变量
        self.data = []  # 初始化临时保存数据的变量

    def startElement(self, name, attrs):  # 重写开始元素的方法
        if name == 'h1':  # 如果是一级标题开始标记
            self.in_headLine = True  # 打开开关

    def characters(self, content):  # 重写元素内容的方法
        if self.in_headLine:  # 如果开关打开
            self.data.append(content)  # 添加内容到临时变量

    def endElement(self, name):  # 重写结束元素的方法
        if name == 'h1':  # 如果是一级标题结束标记
            content = ''.join(self.data)  # 提取内容为临时变量中保存的所有内容
            self.data = []  # 清空临时变量
            self.headLines.append(content)  # 标题列表中添加提取到的内容
            self.in_headLine = False  # 关闭开关


if __name__ == '__main__':
    headLines = []  # 创建空的一级标题列表
    parse('website.xml', HeadLineHandler(headLines))  # 调用解析方法
    for line in headLines:  # 遍历通过解析写入内容的一级标题列表
        print(line)  # 显示输出每一个标题内容

运行上方代码,显示结果为:

欢迎访问小楼的个人网站!
您正在访问文章列表页!
您正在访问资源下载页!
您正在访问文档列表页!

代码比较简单,大家参照注释进行理解基本就能够明白整个程序的运行过程。

这里面有两个关键点:

  • 变量in_headLine是一个开关,读取到一级标题的开始标记时,打开这个开关,并且将开关打开时读取到的内容提取;当读取到一级标题的结束标记时,关闭这个开关,以免其他内容被提取。
  • 变量data是一个临时保存提取内容的列表,在上方代码中并未发挥实际作用,它的作用是将某一对标记之间的多段数据内容顺序保存。只有当一对标记间存在其它标记时,才会出现多段内容,当前练习项目中不存在这种情况。

通过前面两段代码,我们已经对XML解析有了一些了解。

下面,我们就完成解析XML代码并生成HTML页面的功能。

为了避免混乱,我们可以新建一个Python文件。

实现的思路为:

  • 读取开始标记时,如果是页面标记(page),创建页面文件,写入页头HTML代码;并打开写入页面内容的开关;
  • 读取开始标记时,如果写入页面内容开关被打开,原样写入该开始标记的名称与属性到页面文件;
  • 读取标记内容时,如果写入页面内容开关被打开,直接写入内容到页面文件;
  • 读取结束标记时,如果是页面标记,关闭写入页面内容的开关,写入页脚HTML代码,关闭页面文件;
  • 读取结束标记时,如果写入页面内容开关被打开,直接写入结束标记。

示例代码:

from  xml.sax import parse
from xml.sax.handler import ContentHandler
'''
想要学习Python?Python学习交流群:984632579满足你的需求,资料都已经上传群文件,可以自行下载!
'''
class MakePages(ContentHandler):
    in_page = False  # 定义开关变量

    def startElement(self, name, attrs):  # 重写开始元素的方法
        if name == 'page':  # 如果是页面标记
            self.in_page = True  # 打开开关
            self.file = open(attrs['name'] + '.html', 'w')  # 创建HTML文件
            self.file.write('<html>\n<head>\n<meta charset="gbk">\n<title>{}</title>\n</head>\n<body>\n'
                            .format(attrs['title']))  # 写入页头HTML代码
        elif self.in_page:  # 如果开关被打开
            self.file.write('<' + name)  # 写入标记开始符号与名称
            for key, value in attrs.items():  # 遍历属性集合
                self.file.write(' {}="{}"'.format(key, value))  # 写入属性的键值
            self.file.write('>')  # 写入标记末尾符号

    def characters(self, content):  # 重写元素内容的方法
        if self.in_page:  # 如果开关被打开
            self.file.write(content)  # 写入内容

    def endElement(self, name):  # 重写结束元素的方法
        if name == 'page':  # 如果是页面标记
            self.in_page = False  # 关闭开关
            self.file.write('\n</body>\n</html>\n')  # 写入页脚HTML代码
            self.file.close()  # 关闭创建的页面文件
        elif self.in_page:  # 如果开关被打开
            self.file.write('</' + name + '>')  # 写入结束标记

if __name__ == '__main__':
    parse('website.xml', MakePages())  # 调用解析函数

运行以上代码后,虽然会在项目文件夹中出现4个html文件,但是明显和我们的预期结果不一样。

我们希望能够将除了“index.html”页面之外的页面放在名为“catalog”目录中。

那么,如何解决这个问题呢?

在下一篇教程中,我带大家一起再次实现这个功能,让它能够完美实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值