0

我正在尝试使用本教程的“事件驱动解析”部分中描述的模式。lxml

在我的代码中,我正在调用一个可以使用该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 elemor for c in list(elem),效果相同。
  • 我需要使用迭代方法来保持低内存使用率。
  • 在实际用例中,我正在使用属性进行元素查找:

    if elem.attrib.get('id') == elem_id:
        return _get_info(elem)
    

我想解释一下如何clear在处理内部元素之前设法擦除它们,以及如何在需要处理祖先时将它们保存在内存中。

4

1 回答 1

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',)):
    ...

但是,链接的文档说,

请注意,在收到开始事件时,元素的文本、尾部和子项不一定存在。只有结束事件才能保证元素已被完全解析。

这可能意味着应该处理start和事件以允许仅在安全时调用。我正在考虑实现某种堆栈并在事件中推送东西,在事件中弹出和ing 。endclear()startclearend

我非常欢迎使用这个或其他想法显示实现的答案。

注意:最简单的方法就是将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()
于 2012-09-11T12:35:01.263 回答