导读:本期聚焦于沙月恵奈‌创作的《Python如何处理xml数据?使用ElementTree模块读取和修改xml详解》,敬请观看详情。xml文件在配置管理、数据交换等场景中随处可见,Python标准库自带的xml.etree.ElementTree模块无需额外安装就能完成解析、查找、修改和写入等操作,是处理xml数据的首选方案。本文将从xml的基本结构讲起,带你了解ElementTree的树形模型与解析方式,再通过遍历节点、find与findall精准查找元素、修改标签文本与属性、动态增删子节点等实例,逐步演示完整的读写流程,最后附上保存文件与常见踩坑点的处理办法,帮你快速上手xml数据处理。

xml作为一种可扩展标记语言,至今仍广泛应用于配置文件、接口报文、数据存储等领域。Python标准库中的xml.etree.ElementTree模块(以下简称ElementTree)提供了轻量且高效的API,不需要安装任何第三方库就可以完成xml的解析、查找、修改与生成。这篇文章用实际代码演示ElementTree的完整用法,读完即可独立处理常见的xml数据任务。

Python如何处理xml数据?使用ElementTree模块读取和修改xml详解

一、先搞清楚xml的树形结构模型

在动手写代码之前,需要先理解ElementTree看待xml文件的方式。一个xml文件会被解析成一棵由Element节点组成的树,根节点只有一个,下面可以挂任意层级的子节点。以下面这个简单的配置文件为例:

<?xml version="1.0" encoding="utf-8"?>
<config>
    <database host="127.0.0.1" port="3306">
        <username>root</username>
        <password>123456</password>
    </database>
    <debug>true</debug>
</config>

这段xml中,<config>是根节点,<database>是它的子节点,并且携带了host和port两个属性。<username>这类叶子节点包含文本内容。ElementTree把每一个节点抽象成Element对象,它有四个最常用的属性:tag表示标签名,attrib是以字典形式存放的属性,text是标签之间的文本,tail是标签结束之后的文本。理解这四个属性,后面所有的查找和修改操作都会变得非常直观。

还要注意一点,属性和子节点是两种不同的数据组织方式。像host和port这种成对出现的键值,写成属性更紧凑;而username这种可能包含结构化内容的,写成子节点更灵活。读取时两者使用的API完全不同,属性通过attribget()方法访问,子节点则通过遍历或查找方法获取。

二、解析xml文件的几种方式

ElementTree提供了两种最基础的解析入口:一是ET.parse(),接收文件路径,返回ElementTree对象;二是ET.fromstring(),接收字符串,直接返回根Element对象。前者适合处理磁盘上的文件,后者常用于处理网络接口返回的xml报文。

import xml.etree.ElementTree as ET

# 方式一:从文件解析
tree = ET.parse('config.xml')
root = tree.getroot()

# 方式二:从字符串解析
xml_str = '<config><debug>true</debug></config>'
root2 = ET.fromstring(xml_str)

print(root.tag)        # 输出: config
print(root.attrib)     # 输出: {}

拿到根节点后,ET.parse()返回的tree对象还负责后续写回文件的操作,而fromstring()返回的只是Element,如果要保存,需要先用ET.ElementTree(root)包装一层。这是初学者经常混淆的地方,建议根据数据来源选择:文件操作走parse,字符串操作走fromstring。

对于特别大的xml文件(比如几百MB的日志导出),一次性加载整棵树会占用大量内存,这时可以改用ET.iterparse()进行增量解析。它按顺序逐个事件地处理节点,处理完的节点可以调用elem.clear()释放内存,实测可以把内存占用控制在很低的水平。不过日常处理中小型配置文件,直接用parse就够了。

三、遍历与查找节点的方法

最直接的遍历方式是for循环,配合iter()方法可以递归遍历所有后代节点。看下面的例子:

import xml.etree.ElementTree as ET

tree = ET.parse('config.xml')
root = tree.getroot()

# 只遍历直接子节点
for child in root:
    print(child.tag, child.attrib)

# 递归遍历所有层级,找出全部username节点
for elem in root.iter('username'):
    print(elem.tag, elem.text)

如果目标明确,用find()findall()配合xpath风格的路径表达式效率更高。find()返回第一个匹配的元素,找不到返回None;findall()返回所有匹配元素组成的列表。路径表达式中,直接写标签名表示查找直接子节点,前面加.//表示在整个子树中递归查找。

import xml.etree.ElementTree as ET

root = ET.parse('config.xml').getroot()

# 查找直接子节点
db = root.find('database')
print(db.get('host'))          # 输出: 127.0.0.1
print(db.get('port', '3306'))  # 第二个参数是默认值

# 递归查找所有username
users = root.findall('.//username')
for u in users:
    print(u.text)

ElementTree还支持有限的xpath语法,例如[@host='127.0.0.1']可以按属性筛选:root.findall(".//database[@host='127.0.0.1']")。需要完整xpath功能的话,可以安装第三方库lxml,它的API与ElementTree高度兼容,切换成本很低。

四、修改节点内容与属性

修改操作同样简单。改文本就是给text赋值,改属性用set(),删除属性用pop()操作attrib字典即可。

import xml.etree.ElementTree as ET

tree = ET.parse('config.xml')
root = tree.getroot()

# 修改文本
db = root.find('database')
db.find('password').text = 'newpass123'

# 修改和删除属性
db.set('port', '3307')
db.attrib.pop('host')

tree.write('config.xml', encoding='utf-8', xml_declaration=True)

动态构建新的xml结构时,用ET.SubElement()在指定父节点下创建子元素,用remove()删除子元素。最后调用tree的write()方法保存,参数xml_declaration=True会在文件头部写入xml声明,encoding='utf-8'确保中文不乱码,这两个参数建议始终带上。

import xml.etree.ElementTree as ET

root = ET.Element('students')
s1 = ET.SubElement(root, 'student', {'id': '1001'})
ET.SubElement(s1, 'name').text = '张三'
ET.SubElement(s1, 'score').text = '92'

# 删除score节点
s1.remove(s1.find('score'))

ET.ElementTree(root).write('students.xml',
                          encoding='utf-8',
                          xml_declaration=True)

有一个体验上的小缺陷要提一下:ElementTree的write方法默认不做缩进,整个文件会挤在一行。Python 3.9之后可以直接调用ET.indent(tree, space=' ')美化格式,老版本则建议用xml.dom.minidom重新格式化一遍再输出。

五、常见踩坑点与注意事项

第一是编码问题。如果xml文件声明是utf-8但实际保存成了gbk,解析时会直接抛出解析异常,遇到这种情况先确认文件真实编码。第二是命名空间,带xmlns的xml在查找时必须把命名空间前缀写进路径,例如root.findall('{http://ippipp.com/ns}item'),直接写item是找不到的,这是处理第三方接口报文时最高频的坑。

第三是安全方面,解析不受信任的xml数据时要警惕xml外部实体注入(XXE)攻击。虽然标准库的ElementTree默认不解析外部实体,相对安全,但换用lxml时务必关闭resolve_entities相关选项。第四,find()返回None时不做判断就访问属性会触发AttributeError,建议养成先判空再取值的习惯。

总结一下,ElementTree覆盖了xml处理的绝大多数场景:parse和fromstring负责读取,find与findall负责定位,text、set、SubElement负责修改和构建,write负责落盘。把这些API组合起来,配置文件读写、接口报文解析这类任务都能轻松搞定。只有在需要完整xpath或高性能处理超大文件时,才需要考虑lxml这样的第三方方案。

Python xml解析ElementTreexml数据读写修改时间:2026-09-03 15:35:05

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49650.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。