find只返回第一个匹配的直接子元素,未找到则返回None;findall返回所有匹配子节点的列表,未找到返回空列表;二者均不支持完整XPath语法,且查找范围默认为直接子元素。

find 只返回第一个匹配节点,找不到就返回 None
这是最常被误用的点:很多人以为 find 会抛异常或返回空列表,结果在调用 .text 或 .attrib 时直接报 AttributeError: 'NoneType' object has no attribute 'text'。它只找**第一个**符合 XPath 的子元素(注意:仅限直接子元素,除非用 //),没找到就安静地返回 None。
实操建议:
- 用前必须判空:
elem = root.find('tag'); if elem is not None: ... - 想查任意层级的第一个匹配项?写成
root.find('.//tag'),但注意性能略低 - 别拿
find当“是否存在”的布尔判断——它返回的是节点或None,不是True/False
findall 返回所有匹配子节点的列表,空列表不报错
findall 的行为更“友好”:只要 XPath 合法,它一定返回 list——哪怕一个都没匹配上,也是空列表 []。这让你能安全地用 for elem in root.findall('item') 或检查 len(...)。
但要注意它的查找范围仍是**直接子元素**,不会自动递归。常见误解是以为 findall('tag') 能扫完整棵树。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 要跨层级查全部
item?写root.findall('.//item'),但注意 ElementTree 对//支持有限(不支持谓词如//item[@id='1']) - 想按属性筛选?
root.findall(".//*[@lang='en']")可以,但@前必须有标签名或通配符 - 性能敏感场景慎用
.//——它会遍历整个子树,比find或findall的单层查找慢不少
find 和 findall 都不支持完整 XPath 语法
ElementTree 的 XPath 是阉割版:没有 following-sibling::、不支持函数如 contains()、不能用变量或命名空间前缀(除非手动注册)。你写的 find("tag[1]") 或 find("tag[last()]") 全部无效——它只认最基础的标签名、属性匹配和 //。
实操建议:
- 需要复杂定位?先用
findall拿到候选列表,再用 Python 过滤:[e for e in root.findall('item') if e.get('type') == 'urgent'] - 依赖命名空间?必须用
{http://example.com}tag这种带 URI 的写法,或提前设namespaces={'ex': 'http://example.com'}并在 XPath 中用ex:tag - 别指望
find('.//item[@id]')能排除无id属性的节点——它只匹配有该属性的,但 ElementTree 不支持@id='x'以外的属性条件组合
用 getroot().find() 还是 tree.find()?路径起点决定一切
很多人卡在“为什么明明 XML 里有 data 标签,tree.find('data') 却返回 None?”——因为 tree 是 ET.ElementTree 对象,它的 find 方法默认从根节点开始查;但如果你写的是 tree.find('data'),而实际根节点叫 root,里面才包着 data,那当然找不到。
实操建议:
- 统一用
root = tree.getroot(),之后所有查找都基于root,避免混淆 -
root.find('child')查的是root的直接子节点;root.find('./child')效果一样,但显式写出.更易读 - 绝对路径?ElementTree 不支持
/root/child这种写法,/开头会被忽略——一律当相对路径处理
find 和 findall 时,最麻烦的其实是 XPath 表达能力不足和错误静默——它不报错,也不提示你写错了语法,只是默默返回 None 或空列表。调试时多打一行 print([e.tag for e in root]) 看实际结构,比反复猜 XPath 有效得多。

















