【问题标题】:ElementTree - findall to recursively select all child elementsElementTree - findall 递归选择所有子元素
【发布时间】:2015-07-17 20:12:28
【问题描述】:

Python 代码:

import xml.etree.ElementTree as ET
root = ET.parse("h.xml")
print root.findall('saybye')

h.xml 代码:

<hello>
  <saybye>
   <saybye>
   </saybye>
  </saybye>
  <saybye>
  </saybye>
</hello>

代码输出,

[<Element 'saybye' at 0x7fdbcbbec690>, <Element 'saybye' at 0x7fdbcbbec790>]

saybye 是另一个saybye 的子代,此处未选择。那么,如何指示 findall 递归遍历 DOM 树并收集所有三个 saybye 元素?

【问题讨论】:

    标签: python xml python-2.7 recursion


    【解决方案1】:

    从 2.7 版本开始,您可以使用xml.etree.ElementTree.Element.iter

    import xml.etree.ElementTree as ET
    root = ET.parse("h.xml")
    print root.iter('saybye')
    

    19.7. xml.etree.ElementTree — The ElementTree XML API

    【讨论】:

    • 不幸的是他们忘记了那个人的命名空间
    • @kassiopeia:我不确定我理解你的意思。你能帮帮我吗?
    • 在 python 3 中,任何函数 find()findall()findtext() 甚至 iterfind() 都有一个可选的 namespaces 参数来指定带有命名空间的字典。只有iter() 没有。见:docs.python.org/3/library/…
    【解决方案2】:

    如果你不怕一点 XPath,你可以使用 // 语法,意思是找到任何后代节点:

    import xml.etree.ElementTree as ET
    root = ET.parse("h.xml")
    print(root.findall('.//saybye'))
    

    不支持完整的 XPath,但以下是支持的列表: https://docs.python.org/2/library/xml.etree.elementtree.html#supported-xpath-syntax

    【讨论】:

      【解决方案3】:

      引用findall

      Element.findall() 只查找带有标签且是当前元素的直接子元素的元素。

      由于它只找到直接的孩子,我们需要递归地找到其他孩子,像这样

      >>> import xml.etree.ElementTree as ET
      >>> 
      >>> def find_rec(node, element, result):
      ...     for item in node.findall(element):
      ...         result.append(item)
      ...         find_rec(item, element, result)
      ...     return result
      ... 
      >>> find_rec(ET.parse("h.xml"), 'saybye', [])
      [<Element 'saybye' at 0x7f4fce206710>, <Element 'saybye' at 0x7f4fce206750>, <Element 'saybye' at 0x7f4fce2067d0>]
      

      更好的是,让它成为一个生成器函数,像这样

      >>> def find_rec(node, element):
      ...     for item in node.findall(element):
      ...         yield item
      ...         for child in find_rec(item, element):
      ...             yield child
      ... 
      >>> list(find_rec(ET.parse("h.xml"), 'saybye'))
      [<Element 'saybye' at 0x7f4fce206a50>, <Element 'saybye' at 0x7f4fce206ad0>, <Element 'saybye' at 0x7f4fce206b10>]
      

      【讨论】:

      • 以这种方式,您返回 &lt;saybye&gt; 节点,这些节点要么是根的直接子节点,要么具有 &lt;saybye&gt; 作为父节点,因为您不会遍历整个树。
      【解决方案4】:

      Element.findall() 只查找带有标签且是当前元素的直接子元素的元素。

      我们需要递归遍历所有子元素以找到与您的元素匹配的元素。

      def find_rec(node, element):
          def _find_rec(node, element, result):
              for el in node.getchildren():
                  _find_rec(el, element, result)
              if node.tag == element:
                  result.append(node)
          res = list()
          _find_rec(node, element, res)
          return res
      

      【讨论】:

        猜你喜欢
        • 2011-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多