我正在尝试使用lxml
教程的"event-driven parsing"部分中描述的模式。
在我的代码中,我正在调用一个可以使用iterchildren()
方法递归运行元素的函数。我将在这里使用两个嵌套循环进行说明。
这可以按预期工作:
xml = StringIO("<root><a><b>data</b><c><d/></c></a><a><z/></a></root>")
for ev, elem in etree.iterparse(xml):
if elem.tag == 'a':
for c in elem.iterchildren():
for gc in c.iterchildren():
print gc
输出为<Element d at 0x2df49b0>
。
但如果我最后添加.clear()
:
for ev, elem in etree.iterparse(xml):
if elem.tag == 'a':
for c in elem.iterchildren():
for gc in c.iterchildren():
print gc
elem.clear()
- 它不会打印任何东西。为什么会如此?如何解决这个问题?
注意:
iterchildren
并for c in elem
或for c in list(elem)
,效果相同。在实际使用案例中,我正在使用属性进行元素查找:
if elem.attrib.get('id') == elem_id:
return _get_info(elem)
我想解释clear
如何在处理内部元素之前将其擦除,以及如何在处理祖先时将它们保存在内存中。
答案 0 :(得分:2)
问题是默认iterparse
会产生end
个事件。对于子元素end
,事件的生成时间早于其祖先:
>>> for ev, elem in etree.iterparse(xml):
print elem
<Element b at 0x38fe320>
<Element d at 0x38fe0f0>
<Element c at 0x38fe2d0>
<Element a at 0x38fe190>
<Element z at 0x38fe230>
<Element a at 0x38fe3c0>
<Element root at 0x2df48c0>
在这个简单的情况下,可以通过依赖start
事件来解决问题:
for ev, elem in etree.iterparse(xml, events=('start',)):
...
然而,链接的文档说,
请注意,Element的text,tail和children不是 在接收开始事件时必然存在。只有结束 event保证Element已被完全解析。
这可能意味着应该处理start
和end
个事件,以便只有在安全的情况下才能调用clear()
。我正在考虑实施某种堆栈并在start
个事件上推送,弹出和clear
end
个事件。
我非常欢迎使用这个或其他想法展示实现的答案。
注意:最简单的方法就是将clear
调用缩进if
。这将允许访问孙子,但所有不相关的元素将保持不clear
。
修改强>
我目前正在以下列方式使用状态保持变量:
found = False
for event, elem in etree.iterparse(source, events=('start', 'end')):
if event == 'start':
if elem.attrib.get('id') == elem_id:
found = True
else:
if elem.attrib.get('id') == elem_id:
return _get_info(elem)
if not found:
elem.clear()