在lxml

时间:2017-01-20 14:35:08

标签: python lxml

背景:我正在尝试使用从Web服务端点检索的元数据来充实XML报告。该报告列出了文本模块和图形,每个图形都有几种分辨率。我无法为每个分辨率添加元数据。

问题:这是简化的问题。

from lxml import etree as ET

myxml = """\
<report>
    <object id="foo">
        <reportitems>
            <reportitem id="1"/>
            <reportitem id="2"/>
            <reportitem id="3"/>
        </reportitems>
    </object>
</report>
"""

report = ET.fromstring(myxml)
test = ET.Element("test", foo="bar")

for r in report.findall("object/reportitems/reportitem"):
    r.append(test)

我得到了这个输出:

<report>
    <object id="foo">
        <reportitems>
            <reportitem id="1"/>
            <reportitem id="2"/>
            <reportitem id="3"><test foo="bar"/></reportitem>
        </reportitems>
    </object>
</report>

现在,如果我像这样修改代码(使用相同的XML代码段):

report = ET.fromstring(myxml)
myElements = [ET.Element("test1"), ET.Element("test2"), ET.Element("test3")]

counter = 0
for r in report.findall("object/reportitems/reportitem"):
    r.append(myElements[counter])
    counter += 1

...然后我得到了这个输出:

<report>
    <object id="foo">
        <reportitems>
            <reportitem id="1"><test1/></reportitem>
            <reportitem id="2"><test2/></reportitem>
            <reportitem id="3"><test3/></reportitem>
        </reportitems>
    </object>
</report>

为什么我不能将与孩子相同(相同)的元素添加到我迭代的多个元素中?

2 个答案:

答案 0 :(得分:4)

lxml tutorial

中描述了此行为
  

还有另一个重要的案例,即Elements中的行为   lxml(在2.0及更高版本中)与列表和列表的不同   原始的ElementTree(1.3版之前或Python 2.7 / 3.2之前):

>>> for child in root:
...     print(child.tag)
child0 child1 child2 child3
>>> root[0] = root[-1]  # this moves the element in lxml.etree!
>>> for child in root:
...     print(child.tag)
child3 child1 child2
  

在此示例中,最后一个元素被移动到不同的位置,   而不是被复制,即它自动从其中删除   将它放在不同的地方以前的位置。在列表中   对象可以同时出现在多个位置,而且   上面的赋值只会将项目引用复制到第一个   位置,以便两者包含完全相同的项目:

>>> l = [0, 1, 2, 3]
>>> l[0] = l[-1]
>>> l
[3, 1, 2, 3]
  

请注意,在原始的ElementTree中,单个Element对象可以位于任意数量树中的任意数量的位置,   它允许与列表相同的复制操作。显而易见的   缺点是对这种元素的修改将适用于所有人   它出现在树中的地方,可能是也可能不是。   这种差异的好处是lxml.etree中的元素总是如此   只有一个父,可以通过getparent()查询   方法。原始的ElementTree不支持此功能。

>>> root is root[0].getparent()  # lxml.etree only!
True
  

如果要将元素复制到lxml.etree中的其他位置,请考虑   使用Python的复制模块创建独立的深层副本   标准库:

>>> from copy import deepcopy

>>> element = etree.Element("neu")
>>> element.append( deepcopy(root[1]) )

>>> print(element[0].tag)
child1
>>> print([ c.tag for c in root ])
['child3', 'child1', 'child2']

答案 1 :(得分:0)

构造函数ET.Element中的问题每次调用只创建一个节点。您可以更改节点的父节点,但ET.Element将只有一个节点。您可以在循环中多次创建ET.Element以避免此问题:

for r in report.findall("object/reportitems/reportitem"):
    node = ET.Element("test", foo="bar")
    r.append(node)