ElementTree默认丢弃处理指令(PI),因其定位为轻量级数据处理器;需用XMLParser配自定义Target类捕获PI,或改用支持PI的lxml.etree。

ElementTree 默认会丢弃处理指令(PI)
Python 标准库的 xml.etree.ElementTree 在解析 XML 时,默认跳过所有处理指令(如 、),既不保留在树中,也不提供回调接口。这不是 bug,而是设计取舍:ElementTree 定位是“轻量级、面向数据的 XML 处理”,而非完整 XML 1.0 合规解析器。
想保留 PI,必须用 XMLParser 配合自定义 target
标准方式是绕过高层 API,手动构造 XMLParser,传入实现了 start_pi 和 end_pi 方法的 Target 对象。注意:start_pi 的参数是 (target, name, data),其中 name 是处理指令目标名(如 "xml-stylesheet"),data 是其内容字符串(不含前后空格,但含内部所有空白)。
from xml.etree import ElementTree as ETclass PITarget: def init(self): self.pis = [] def start_pi(self, target, data): self.pis.append((target, data)) def end_pi(self): pass def start_element(self, args): pass def end_element(self, args): pass def data(self, data): pass def close(self): return None
parser = ET.XMLParser(target=PITarget()) with open("doc.xml", "rb") as f: result = parser.close() # 注意:不是 parser.parse()
result.pis 现在包含所有 PI 元组
lxml 是更现实的选择
如果你实际需要可靠地读写带 PI 的 XML(比如 XHTML 文档头部的样式表声明、SVG 中的 XML 声明扩展),lxml.etree 是更可行的方案。它原生支持 PI 节点,可被当作普通元素操作:
-
etree.ProcessingInstruction类型节点存在于.getchildren()或迭代结果中 - 可用
tree.iter("{http://www.w3.org/2000/xmlns/}processing-instruction")查找(注意命名空间前缀) - 序列化时默认保留 PI,无需额外配置
代价是引入第三方依赖,但对生产环境中的 XML 处理来说,这通常是值得的。
立即学习“Python免费学习笔记(深入)”;
别试图用正则或字符串替换补救
常见误区是先用 ET.tostring() 得到 bytes,再用正则插入 PI——这极易破坏编码声明位置(XML 声明必须在第一行)、损坏 UTF-8 BOM、或导致后续解析失败。ElementTree 的输出不保证格式稳定,且不处理 PI 重排逻辑。真要插 PI,应直接操作底层 lxml 的节点树,或在生成原始 XML 字符串阶段就组织好。










