使用 xml.etree.ElementTree 合并同结构 XML 文件时,应创建统一根节点(如 ),逐个解析文件并追加其子元素;需处理命名空间、属性保留、大文件流式解析(iterparse)及 UTF-8 编码写入,避免字符串拼接。

用 Python 的 xml.etree.ElementTree 合并同结构 XML 文件
多个 XML 文件结构一致(比如都是 ),想把内容合到一个根节点下,xml.etree.ElementTree 是最轻量、无需安装依赖的选择。
关键不是“拼字符串”,而是解析后操作元素树:读取每个文件的根子元素,逐个追加到新根节点下。
- 必须统一目标根名,比如新建
etree.Element("data"),不能直接用第一个文件的根名——否则后续文件的根名不匹配会出错 - 所有源文件的根节点应只含数据子节点(如
),不含元信息;如果源根有属性(如),得手动复制或丢弃,否则合并后属性会丢失 - 别用
tree.write()默认编码写入中文,容易变UnicodeEncodeError;显式指定encoding="utf-8"和xml_declaration=True
import xml.etree.ElementTree as etree
root = etree.Element("data")
for file in ["a.xml", "b.xml", "c.xml"]:
tree = etree.parse(file)
for child in tree.getroot():
root.append(child)
etree.ElementTree(root).write("merged.xml", encoding="utf-8", xml_declaration=True)处理不同根名或带命名空间的 XML 合并
遇到源文件根名不一致(如 和 )或含命名空间(xmlns="http://example.com/ns"),直接 getroot() 会失败或找不到子节点。
本质是命名空间影响了 XPath 解析和元素匹配,必须显式处理。
- 用
etree.register_namespace()注册常用前缀,或解析时传入parser=etree.XMLParser(resolve_entities=False)避免实体报错 - 获取子节点别用
tree.getroot()[:],改用tree.findall("./*")或更稳的list(tree.getroot())——后者对带默认命名空间的文档也有效 - 若需保留原根的属性(如
),不能只追加子节点,得提前提取属性字典,再root.attrib.update(...)
大文件合并时内存爆掉怎么办
单个 XML 超 50MB,用 etree.parse() 会一次性加载全部 DOM 到内存,容易 OOM。这时候得切到流式解析。
xml.sax 太底层,推荐 iterparse:边读边处理,只保留当前需要的节点。
- 用
etree.iterparse(file, events=("start", "end")),监听"end"事件抓取每个完整闭合后立刻追加,然后调用elem.clear()清空已处理节点内存 - 别在循环里反复
etree.ElementTree(root).write(),累积完再写一次;否则磁盘 I/O + 编码开销翻倍 - 注意
iterparse不自动处理命名空间声明,需手动从event == "start-ns"中提取并注册
Shell 下快速合并(仅限简单扁平 XML)
只是把多个文件的 body 拼一起,且确定没嵌套、无属性、无注释,Linux/macOS 可用命令行应急,但非常脆弱。
核心是删头去尾、保留中间内容,再包一层新根。
- 用
sed -n '/抽每段,但遇换行或属性就失效;稳妥点用/,//p' awk '//,//' *.xml - 必须补全 XML 声明和根:
echo ''; awk '...' *.xml; echo '' - 一旦某个文件含
或 CDATA,整个结果就非法;生产环境别用
真正要靠得住,还是得回到 Python 脚本——尤其当字段顺序不一致、有可选空标签、或需要去重时,字符串拼接根本不可控。










