使用Python ElementT迭代多个(父、子)节点

2024-10-01 13:28:55 发布

您现在位置:Python中文网/ 问答频道 /正文

用于Python(2.6)的ElementTree的标准实现不提供从子节点指向父节点的指针。因此,如果需要父母,建议对父母而不是孩子进行循环。

假设我的xml是这样的:

<Content>
  <Para>first</Para>
  <Table><Para>second</Para></Table>
  <Para>third</Para>
</Content>

以下查找不考虑父节点的所有“Para”节点:

(1) paras = [p for p in page.getiterator("Para")]

这(改编自effbot)通过在父节点而不是子节点上循环来存储父节点:

(2) paras = [(c,p) for p in page.getiterator() for c in p]

这是完全有意义的,可以通过一个条件来扩展,以实现(假设)与(1)相同的结果,但是添加了父信息:

(3) paras = [(c,p) for p in page.getiterator() for c in p if c.tag == "Para"]

ElementTree documentation建议getiterator()方法执行深度优先搜索。运行它而不查找父(1)会产生:

first
second
third

然而,从第(3)段中提取文本,得到:

first, Content>Para
third, Content>Para
second, Table>Para

这似乎是广度第一。

因此,这提出了两个问题。

  1. 这是正确的和预期的行为吗?
  2. 如果子元素必须是某一类型,但父元素可以是任何类型,则如何提取(父元素,子元素)元组(如果必须维护文档顺序,则为)。我不认为运行两个循环并将(3)生成的(父、子)映射到(1)生成的订单是理想的。

Tags: in元素for节点pagetablecontentfirst
1条回答
网友
1楼 · 发布于 2024-10-01 13:28:55

考虑一下:

>>> xml = """<Content>
...   <Para>first</Para>
...   <Table><Para>second</Para></Table>
...   <Para>third</Para>
... </Content>"""
>>> import xml.etree.cElementTree as et
>>> page = et.fromstring(xml)
>>> for p in page.getiterator():
...     print "ppp", p.tag, repr(p.text)
...     for c in p:
...         print "ccc", c.tag, repr(c.text), p.tag
...
ppp Content '\n  '
ccc Para 'first' Content
ccc Table None Content
ccc Para 'third' Content
ppp Para 'first'
ppp Table None
ccc Para 'second' Table
ppp Para 'second'
ppp Para 'third'
>>> 

旁白:列表理解是非常棒的,直到你想清楚什么是被迭代的:-)

getiterator是按广告顺序生成“ppp”元素的。然而,您正在从附属“ccc”元素中提取感兴趣的元素,这些元素不符合您所需的顺序。

一种解决方案是进行自己的迭代:

>>> def process(elem, parent):
...    print elem.tag, repr(elem.text), parent.tag if parent is not None else None
...    for child in elem:
...       process(child, elem)
...
>>> process(page, None)
Content '\n  ' None
Para 'first' Content
Table None Content
Para 'second' Table
Para 'third' Content
>>>

现在,您可以在每个“Para”元素经过时,都引用其父元素(如果有的话)。

这可以很好地包装在发电机小工具中:

>>> def iterate_with_parent(elem):
...     stack = []
...     while 1:
...         for child in reversed(elem):
...             stack.append((child, elem))
...         if not stack: return
...         elem, parent = stack.pop()
...         yield elem, parent
...
>>>
>>> showtag = lambda e: e.tag if e is not None else None
>>> showtext = lambda e: repr((e.text or '').rstrip())
>>> for e, p in iterate_with_parent(page):
...     print e.tag, showtext(e), showtag(p)
...
Para 'first' Content
Table '' Content
Para 'second' Table
Para 'third' Content
>>>

相关问题 更多 >