在Python中处理XML时,带命名空间的文档是很常见的,例如Atom订阅源、Office文件或各类Web接口返回的数据。使用lxml库可以稳妥地解析这类文件,但如果不了解命名空间机制,用普通的标签名去查找往往会找不到节点。下面介绍lxml处理命名空间的具体做法。

理解XML命名空间
XML命名空间通过xmlns属性定义一个URI,用来避免不同来源的同名标签冲突。实际文档中常给命名空间起一个前缀,比如<atom:title>里的atom就是前缀。解析时真正标识标签的是命名空间URI加本地名,而不是前缀。
用lxml解析带命名空间的XML
我们可以使用lxml.etree来读取字符串或文件,并通过命名空间映射进行XPath查询。
from lxml import etree
xml_data = '''<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>示例博客</title>
<entry>
<title>第一篇</title>
</entry>
</feed>'''
# 解析XML
root = etree.fromstring(xml_data.encode('utf-8'))
# 定义命名空间映射,键为前缀,值为命名空间URI
ns = {'atom': 'http://www.w3.org/2005/Atom'}
# 使用带命名空间的XPath查找
titles = root.xpath('//atom:title/text()', namespaces=ns)
print(titles) # 输出: ['示例博客', '第一篇']
忽略前缀直接按本地名查找
如果只想按本地名提取而不关心命名空间,可以用local-name()函数。
# 不依赖前缀,匹配所有本地名为title的节点
local_titles = root.xpath('//*[local-name()="title"]/text()')
print(local_titles) # 输出: ['示例博客', '第一篇']
遍历带命名空间的节点
lxml中每个元素的tag属性是包含命名空间URI的,形如{uri}localname。遍历时可以这样判断:
for elem in root.iter():
# elem.tag可能是'{http://www.w3.org/2005/Atom}title'
if elem.tag.endswith('}title'):
print('找到标题:', elem.text)
处理默认命名空间
很多文档像上面的例子一样使用默认命名空间(没有前缀的xmlns)。此时在XPath里仍需给它配一个自定义前缀,如atom,才能正确查询。不写前缀直接写title是查不到的。
| 场景 | 推荐做法 |
|---|---|
| 已知命名空间URI | 构造ns map并用前缀写XPath |
| 不确定URI | 用local-name()按本地名匹配 |
| 遍历全部节点 | 检查elem.tag的结尾或拆分 Clark Notation |
小结
用lxml处理带命名空间的XML,核心就是理解URI才是标识,前缀只是缩写。通过namespaces参数传映射、或用local-name()忽略命名空间,都能稳定拿到数据。熟练后解析各类标准XML将不再困惑。
PythonlxmlXML_namespace修改时间:2026-07-26 19:03:20