如何用yield递归遍历XML并生成可构建DataFrame的路径与值?
处理深层嵌套XML:生成元素路径与值的DataFrame
我正在处理一个嵌套层级极深的XML文件,元素路径对理解数据至关重要。参考相关Python代码,我能打印出元素的路径和对应值,但没法把结果输出成更易用的格式——目标是构建包含Path和Value列的DataFrame。
示例XML结构
<A> <B>foo</B> <C> <D>On</D> </C> <E>Auto</E> <F> <G> <H>shoo</H> <I>Off</I> </G> </F> </A>
可正常打印路径和值的代码
from lxml import etree root = etree.XML(your_xml_string) def print_path_of_elems(elem, elem_path=""): for child in elem: if not child.getchildren() and child.text: # 叶子节点且有文本 => 打印 print "%s/%s, %s" % (elem_path, child.tag, child.text) else: # 有子元素的节点 => 递归处理 print_path_of_elems(child, "%s/%s" % (elem_path, child.tag)) print_path_of_elems(root, root.tag)
输出结果
/A/B, foo /A/C/D, On /A/E, Auto /A/F/G/H, shoo /A/F/G/I, Off
尝试用yield生成数据但失败的代码
from lxml import etree root = etree.XML(your_xml_string) def yield_path_of_elems(elem, elem_path=""): for child in elem: if not child.getchildren() and child.text: ylddict = {'Path':elem_path, 'Value':child.text} yield(ylddict) else: # 有子元素的节点 => 递归处理 yield_path_of_elems(child, "%s/%s" % (elem_path, child.tag)) for i in yield_path_of_elems(root): # 示例中打印,实际要转为DataFrame print(i)
测试发现用yield或return时递归无法正常工作。
解决方案
问题核心是递归调用生成器时,没有传递子生成器的结果。你需要用yield from(Python 3.3+)或者手动迭代子生成器来yield其返回值。
修正后的代码
from lxml import etree import pandas as pd root = etree.XML(your_xml_string) def yield_path_of_elems(elem, elem_path=""): # 拼接当前节点的路径,避免开头出现多余斜杠 current_path = f"{elem_path}/{elem.tag}" if elem_path else elem.tag for child in elem: # 判断是否为有效叶子节点:无后代元素,且有非空文本 if not child.getchildren() and child.text and child.text.strip(): full_path = f"{current_path}/{child.tag}" yield {'Path': full_path, 'Value': child.text.strip()} else: # 递归处理子节点,用yield from传递生成器结果 yield from yield_path_of_elems(child, current_path) # 生成数据并转为DataFrame data_list = list(yield_path_of_elems(root)) df = pd.DataFrame(data_list) print(df)
关键修改说明
yield from处理递归:确保子生成器的每个结果都能被外层生成器输出,解决递归时无返回值的问题- 路径优化:修正路径拼接逻辑,避免出现类似
//A/B的错误格式 - 空文本过滤:用
child.text.strip()过滤掉无实际内容的节点 - 直接生成DataFrame:将生成器转为列表后,直接传入pandas创建DataFrame
输出结果
Path Value 0 /A/B foo 1 /A/C/D On 2 /A/E Auto 3 /A/F/G/H shoo 4 /A/F/G/I Off
Python 2.x兼容版本
如果使用Python 2.x(不支持yield from),可手动循环子生成器:
else: for item in yield_path_of_elems(child, current_path): yield item
内容的提问来源于stack exchange,提问作者richtopia
相关产品推荐
相关产品推荐

