et.parse()用于解析文件并返回elementtree对象,et.fromstring()用于解析xml字符串并直接返回根element;二者不可混用,否则引发parseerror。

ET.parse() 和 ET.fromstring() 到底该用哪个?
解析 XML 时选错入口函数,后续所有操作都可能出问题。ET.parse() 读文件,返回 ElementTree 对象;ET.fromstring() 解析字符串,直接返回根 Element。别把文件路径传给 fromstring()——它会把路径当 XML 内容解析,报 ParseError: not well-formed。
- 文件操作(如读取本地
config.xml):用ET.parse("config.xml"),再调.getroot()拿根节点 - 接口返回的 XML 字符串(如
response.text):直接传给ET.fromstring(response.text) -
parse()支持parser参数(比如用XMLParser(resolve_entities=False)防 XXE),fromstring()不支持 - 性能上,如果只是临时解析一小段字符串,
fromstring()更轻量;频繁读大文件,parse()的流式处理更稳
怎么安全地增删子节点,不崩掉父节点引用?
增删节点不是“写完就完”,ET 的节点是强引用对象。常见错误:循环中用 for child in root: 然后调 root.remove(child),结果漏删、报 RuntimeError: dictionary changed size during iteration。
- 删除多个匹配节点:先收集要删的节点,再统一删
to_remove = [elem for elem in root if elem.get("type") == "temp"] for elem in to_remove: root.remove(elem) - 新增节点:用
root.append(<code>new_elem) 或root.insert(i, <code>new_elem);别直接赋值root[0] = new_elem(这会替换,不是插入) - 插入文本节点?别用
append(ET.Element("text")),文本属于Element的.text属性,直接设elem.text = "hello" - 注意:节点一旦被
remove(),它仍存在内存里,但脱离树结构;再调elem.getparent()会返回None
find() / findall() 找不到元素?XPath 表达式没你想得那么自由
find() 和 findall() 只支持极简 XPath:单层标签名、属性过滤("tag[@attr='val']")、通配符 "*"。不支持 //、ancestor::、函数调用,也不跨层级自动搜索。
- 要找任意深度的
item:不能写root.findall("//item")(会静默失败),改用root.iter("item") - 属性值含空格或特殊字符?用引号包裹:
find('book[@category="sci-fi"]'),单双引号均可,但必须一致 - 匹配多个属性?XPath 不支持
and,只能嵌套:先find('book[@category="tech"]'),再对结果调.find('author[@lang="en"]') - 命名空间(如
<rss xmlns="<a href=" https:>http://purl.org/rss/1.0/"></rss>)必须注册前缀,否则find("channel")一定为空:ns = {"rss": "http://purl.org/rss/1.0/"} root.find("rss:channel", ns)
修改后保存 XML,中文乱码或格式全垮怎么办?
tree.write() 默认用 ASCII 编码,且不缩进、不换行。直接写会导致中文变 中文,或者所有标签挤成一行,Git diff 失效。
立即学习“Python免费学习笔记(深入)”;
- 保存中文:显式指定
encoding="utf-8"和xml_declaration=Truetree.write("out.xml", encoding="utf-8", xml_declaration=True) - 想带缩进?标准库 ET 不提供 自动美化功能。别信网上那些“加
method="xml"就能缩进”的说法——那是 lxml 的行为。ET 原生只输出扁平 XML - 如果真需要可读格式,要么手动拼接换行缩进(不推荐),要么换用
lxml.etree(兼容 ET 接口,多一行from lxml import etree as ET即可,然后用ET.indent(tree)) - 还有个坑:
write()默认覆盖文件,没备份机制。生产环境写关键配置前,建议先shutil.copy("config.xml", "config.xml.bak")
ET 的设计哲学是“够用就好”,不是为了覆盖所有 XML 场景。它快、轻、无依赖,但 XPath 能力弱、不支持 DTD/Schema 验证、写入格式控制差——这些地方卡住时,别硬扛,该换 lxml 就换。真正麻烦的从来不是语法,而是你忘了它根本没实现你脑补的那个功能。










