详解在Python程序中解析并修改XML内容的方法
在实际开发中,经常需要动态修改XML格式的配置文件。本文基于Python标准库xml.etree.ElementTree,详细介绍如何通过路径定位、属性匹配等功能,实现XML节点及子节点的增删改查、文本内容更新,并提供完整可运行的代码示例与保存输出方法,帮助开发者快速掌握自动化配置管理技巧。
需求场景与实现思路
在实际运维或软件配置管理中,通常需要以编程方式实时调整XML配置文件。核心诉求包括:
- 增加或删除特定的节点(如插件、服务定义)
- 增加、删除或修改节点下的属性键值对
- 更新特定节点内的文本内容
Python标准库中的 xml.etree.ElementTree 提供了轻量级的DOM树解析能力。通过将XML文件加载为树状结构后,开发者可以利用XPath路径或属性过滤精准定位目标元素,完成增删改操作后再重新序列化写入磁盘。该方案无需额外依赖,适合轻量级配置热更新场景。
完整操作代码
以下脚本封装了常用操作函数,并演示了从读取、定位、修改到输出的完整流程。代码已适配现代Python 3语法规范。
import xml.etree.ElementTree as ET
def read_xml(in_path):
'''读取并解析xml文件'''
tree = ET.parse(in_path)
return tree
def write_xml(tree, out_path):
'''将xml文件写出,保留声明与编码'''
tree.write(out_path, encoding="utf-8", xml_declaration=True)
def if_match(node, kv_map):
'''判断某个节点是否包含所有传入的属性键值'''
for key, val in kv_map.items():
if node.get(key) != val:
return False
return True
def find_nodes(tree, xpath):
'''查找满足XPath路径的所有节点'''
return tree.findall(xpath)
def get_node_by_keyvalue(nodelist, kv_map):
'''根据属性键值匹配定位节点列表'''
return [node for node in nodelist if if_match(node, kv_map)]
def change_node_properties(nodelist, kv_map, is_delete=False):
'''修改/批量增加 或 删除节点的属性'''
for node in nodelist:
for key, val in kv_map.items():
if is_delete:
if key in node.attrib:
del node.attrib[key]
else:
node.set(key, val)
def change_node_text(nodelist, text, is_add=False, is_delete=False):
'''改变、追加 或 清空节点的文本内容'''
for node in nodelist:
if is_add:
node.text += text
elif is_delete:
node.text = ""
else:
node.text = text
def create_node(tag, property_map, content):
'''创建带属性与文本的新节点'''
elem = ET.Element(tag, property_map)
elem.text = content
return elem
def add_child_node(parent_node, element):
'''将新节点添加到父节点下'''
parent_node.append(element)
def del_node_by_tagkeyvalue(parent_list, tag, kv_map):
'''通过标签名与属性条件定位子节点并删除'''
for parent in parent_list:
for child in parent:
if child.tag == tag and if_match(child, kv_map):
parent.remove(child)
# ================= 主程序执行流 =================
if __name__ == "__main__":
# 1. 读取源文件
tree = read_xml("./test.xml")
# 2. 属性修改
nodes = find_nodes(tree, "processers/processer")
target_nodes = get_node_by_keyvalue(nodes, {"name": "BProcesser"})
change_node_properties(target_nodes, {"age": "1"})
change_node_properties(target_nodes, {"value": ""}, is_delete=True)
# 3. 节点新增
new_child = create_node("person", {"age": "15", "money": "200000"}, "this is the firest content")
for tn in target_nodes:
add_child_node(tn, new_child)
# 4. 删除节点
del_parents = find_nodes(tree, "processers/services/service")
del_node_by_tagkeyvalue(del_parents, "chain", {"sequency": "chain1"})
# 5. 文本修改
text_targets = get_node_by_keyvalue(
find_nodes(tree, "processers/services/service/chain"),
{"sequency": "chain3"}
)
change_node_text(text_targets, "new text")
# 6. 输出结果
write_xml(tree, "./out.xml")
第1步:读取与解析XML文件
调用 read_xml() 将本地XML文件加载至内存,生成带有层级关系的 ElementTree 对象。此步骤为后续所有DOM树操作提供入口,无需手动关闭句柄,解析过程自动处理字符集转换。
第2步:通过路径与属性定位节点
使用 tree.findall(xpath) 可按相对路径筛选节点。例如 "processers/processer" 会返回所有匹配的子节点。进一步通过 get_node_by_keyvalue() 传入字典映射,精确锁定具备特定属性组合的目标元素,避免误操作相邻兄弟节点。
第3步:增删改节点属性与文本内容
属性操作支持批量写入或安全删除。当 is_delete=True 时,仅清除存在于节点attrib字典中的键,不会引发异常。文本更新分为覆盖、追加与清空三种模式,通过布尔参数控制行为逻辑,确保存量数据不被意外覆盖。
第4步:创建子节点与删除指定节点
新建节点需明确标签名、属性字典及初始文本,随后调用 append() 注入目标父节点。删除操作依赖父节点容器,遍历子元素比对标签名与属性条件后执行 parent.remove(child),符合树形结构的安全移除规范。
第5步:序列化并输出新文件
所有内存树结构调整完毕后,调用 write_xml() 将修改后的树对象导出为独立文件。参数 encoding="utf-8" 保证中文与特殊符号正确转义,xml_declaration=True 维持XML头部声明一致性,便于下游系统解析。
修改结果对照与排错建议
执行上述脚本后,目标XML文件的结构与内容将发生如下变化:
<?xml version='1.0' encoding='utf-8'?>
<framework>
<processers>
<processer name="AProcesser" file="lib64/A.so" path="/tmp" />
<processer name="BProcesser" file="lib64/B.so" age="1">
<person age="15" money="200000">this is the firest content</person>
</processer>
<processers> 本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













