Python3 XML 命名空间

🎉摘要:本文详细介绍了XML命名空间的基础知识,包括为什么需要命名空间、如何声明与使用命名空间、默认命名空间,并通过Python的ElementTree和lxml库演示了如何查询、解析及创建带命名空间的XML文档。

命名空间(Namespace)是 XML 中避免元素名冲突的机制,下面将介绍命名空间基础知识,以及 Python 中如何操作命名空间。

为什么需要命名空间

当混合使用多个 XML 词汇表时,元素名可能冲突,如下 <table> 标签名重复了,一个用于 HTML 的表格,一个用于家具的桌子:

<!-- 表格 HTML 的 table -->
<table>
    <tr><td>单元格</td></tr>
</table>

<!-- 家具 XML 的 table -->
<table>
    <name>橡木餐桌</name>
    <width>120cm</width>
</table>

命名空间通过 URI 区分不同来源的元素。也就是在标签前添加前缀,告诉解析器这个标签属于哪个命名空间,类似编程语言中通过包来区分相同的类名。

命名空间声明与使用

在 XML 文档的根标签上面使用 xmlns 添加命名空间,例如:

<?xml version="1.0"?>
<root 
    xmlns:h="http://www.w3.org/1999/xhtml"
    xmlns:f="http://example.com/furniture">
    
    <!-- HTML 表格 -->
    <h:table>
        <h:tr>
            <h:td>单元格</h:td>
        </h:tr>
    </h:table>
    
    <!-- 家具表格 -->
    <f:table>
        <f:name>橡木餐桌</f:name>
        <f:width>120cm</f:width>
    </f:table>
    
</root>

上面文档中声明了两个命名空间如下:

  • h 指向 http://www.w3.org/1999/xhtml

  • f 指向 http://example.com/furniture

其中,<h:table> 标签来自 http://www.w3.org/1999/xhtml 命名空间,而 <f:table> 来自 http://example.com/furniture 命名空间,虽然两个标签都是 <table>,但是通过命名空间进行了区分。h 和 f 是命名空间前缀,用于简化 XML 文档编写。不然,你会写成如下样子:

<http://www.w3.org/1999/xhtml:table>

<http://example.com/furniture:table>

这也太难写了吧!

默认命名空间

不带前缀的元素属于默认命名空间,如下:

<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:media="http://search.yahoo.com/mrss/">
    
    <!-- 属于 Atom 命名空间(无前缀) -->
    <title>我的博客</title>
    
    <!-- 属于 media 命名空间 -->
    <media:thumbnail url="thumb.jpg"/>
    
</feed>

上面文档,xmlns="http://www.w3.org/2005/Atom" 表示默认命名空间,位于默认命名空间下的标签有 <feed>、<title> 标签,因为它们没有指定前缀。

在 Python 中处理命名空间

ElementTree 方案

下面示例演示如何通过 Python 内置的 ElementTree 库操作命名空间,如下:

import xml.etree.ElementTree as ET

# 定义了一个默认命名空间
# 和一个 media 前缀的命名空间
xml_data = '''
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:media="http://search.yahoo.com/mrss/">
    <title>我的博客</title>
    <entry>
        <title>文章标题</title>
        <media:thumbnail url="thumb.jpg"/>
    </entry>
</feed>
'''

# 从字符串加载XML文档
root = ET.fromstring(xml_data)

# 定义命名空间映射
NS = {
    'atom': 'http://www.w3.org/2005/Atom',
    'media': 'http://search.yahoo.com/mrss/'
}

# 使用前缀查询
title = root.find('atom:title', NS)
print(f"标题: {title.text}")

# 查询所有 entry 下的 media:thumbnail
for entry in root.findall('atom:entry', NS):
    thumb = entry.find('media:thumbnail', NS)
    if thumb is not None:
        print(f"缩略图: {thumb.get('url')}")

# 获取实际标签名(包含命名空间 URI)
print(f"根元素标签: {root.tag}")
# 输出: {http://www.w3.org/2005/Atom}feed

运行示例,输出如下:

标题: 我的博客
缩略图: thumb.jpg
根元素标签: {http://www.w3.org/2005/Atom}feed

lxml 方案

下面示例演示如何通过 lxml 库操作命名空间:

from lxml import etree

# 注意 <?xml version="1.0"?> 一定要放在第一行
xml_data = '''<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:media="http://search.yahoo.com/mrss/">
    <title>我的博客</title>
    <media:content url="video.mp4">
        <media:title>视频标题</media:title>
    </media:content>
</feed>
'''

# 从字符串加载 XML 文档
root = etree.fromstring(xml_data.encode('utf-8'))

# 定义命名空间别名
ns = {
    'atom': 'http://www.w3.org/2005/Atom',
    'media': 'http://search.yahoo.com/mrss/'
}

# XPath 支持命名空间
titles = root.xpath('//atom:title/text()', namespaces=ns)
print(f"所有标题: {titles}")

media_titles = root.xpath('//media:title/text()', namespaces=ns)
print(f"媒体标题: {media_titles}")

# 获取元素的命名空间信息
print(f"根元素命名空间: {root.nsmap}")
# 输出: {None: 'http://www.w3.org/2005/Atom', 'media': 'http://search.yahoo.com/mrss/'}

运行示例,输出如下:

所有标题: ['我的博客']
媒体标题: ['视频标题']
根元素命名空间: {None: 'http://www.w3.org/2005/Atom', 'media': 'http://search.yahoo.com/mrss/'}

创建带命名空间的 XML

示例使用 lxml 创建带有命名空间的 XML:

from lxml import etree

# 创建带命名空间的元素
NSMAP = {
    'atom': 'http://www.w3.org/2005/Atom',
    'media': 'http://search.yahoo.com/mrss/'
}

# 创建根元素,声明命名空间,注意 nsmap 参数
root = etree.Element('{http://www.w3.org/2005/Atom}feed', nsmap=NSMAP)

# 添加子元素(使用 Clark 表示法 {URI}localname)
title = etree.SubElement(root, '{http://www.w3.org/2005/Atom}title')
title.text = '我的博客'

# 添加带前缀的元素
content = etree.SubElement(root, '{http://search.yahoo.com/mrss/}content')
content.set('url', 'video.mp4')

# 序列化
print(etree.tostring(root, pretty_print=True, encoding='unicode'))

运行示例,输出如下:

<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
  <atom:title>我的博客</atom:title>
  <media:content url="video.mp4"/>
</atom:feed>

更多知识,请阅读后续章节。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号