Python xml.etree.ElementTree iter 迭代遍历所有子节点

1次阅读

iter()遍历当前元素的所有后代节点(深度优先),非仅直接子节点;想查直接子节点应使用list(elem)或for child in elem:。

Python xml.etree.ElementTree iter 迭代遍历所有子节点

iter() 为什么找不到深层嵌套的子节点

因为 iter() 默认只遍历当前元素及其**所有后代节点**(depth-first),不是“仅直接子节点”——这点和 getchildren()(已弃用)或 list(elem) 完全不同。很多人误以为它像 for child in elem: 那样只扫一层,结果漏掉深层结构。

常见错误现象:elem.iter('tag') 返回空,但手动 inspect 发现目标 tag 其实藏在三层子节点下面;或者遍历时意外匹配到本不该出现的远亲节点。

  • elem.iter() 就是查整棵子树,不设限;想只查直接子节点,改用 list(elem)for child in elem:
  • 指定标签名时,elem.iter('div') 只返回所有名为 div 的后代,不含其他标签;不传参数则返回所有节点(含文本、注释等)
  • 注意命名空间:如果 xmlNamespace(如 {http://www.w3.org/1999/xhtml}div),必须用完整带 ns 的字符串匹配,或提前用 register_namespace 配合通配前缀

iter() 和 findall() / find() 的关键区别在哪

iter() 是迭代器,不建新列表,内存友好;findall() 返回 list,立即求值。但更重要的是语义差异:前者是“从当前节点往下无条件扫全部”,后者是“在直接子节点中按 XPath 查”。哪怕写 elem.findall('.//tag'),也仍是先取子节点再对每个做 XPath 求值,性能和行为都不同于 elem.iter('tag')

  • elem.iter('tag') → 单次 DFS 遍历,快,适合“只要找到所有 tag,不管在哪层”
  • elem.findall('.//tag') → 对每个直接子节点调用 XPath 引擎,有额外开销,且某些老版本 ElementTree 不支持 .// 写法(会报 SyntaxError: cannot use absolute path on element
  • 如果只要最深一层的某个 tag(比如最后一个 item),别用 list(elem.iter('item'))[-1] —— 改用 elem.iterfind('.//item').__next__() 配合 reversed() 或手动循环,避免全量加载

遇到空白文本节点或换行符怎么过滤

XML 解析后,换行、缩进、空格都会变成 texttail 属性为字符串(可能只含 'n ')的 Element 节点。而 iter() 默认会把这些也当节点返回,导致后续逻辑出错,比如 elem.iter() 数出来 12 个节点,实际有意义的只有 5 个。

立即学习Python免费学习笔记(深入)”;

  • 判断是否为纯空白节点:node.text and node.text.strip()node.tag is not None(因为文本节点 tag == None
  • 安全跳过:for node in elem.iter(): if node.tag is None: continue —— 这能滤掉所有文本/注释节点
  • 更稳妥的做法是在解析时就压缩空白:ET.XMLParser(remove_blank_text=True),再传给 ET.parse()ET.fromstring()

python 3.9+ 中 iter() 的兼容性坑

3.9 开始,iter() 的返回类型从 generator 改为 Iterator[Element],表面没区别,但如果你写了类似 nodes = list(elem.iter()); nodes[0].clear(),然后又想继续用 nodes,会发现清空后原节点已变——这不是 bug,是 iterator 的自然行为。更隐蔽的问题是:某些旧工具链(比如用 isinstance(..., collections.Iterator) 做判断)可能误判。

  • 不要反复消费同一个 iter() 结果:它是一次性的,第二次 list(elem.iter()) 才是新遍历
  • 避免对 iter() 结果做索引或切片操作;真要随机访问,先转 list()
  • 跨版本安全写法:[n for n in elem.iter() if n.tag]Filter(Lambda x: x.tag, elem.iter()) 更直观,也避开生成器嵌套陷阱

真正麻烦的不是语法,是 XML 里那些没声明 namespace 却偷偷用了前缀的文档,还有混着 CDATA 和实体引用的 text 内容——这些地方 iter() 照样返回节点,但你得自己判断 node.text 是原始字符串还是已被解析过的值。

text=ZqhQzanResources