删除太多元素

时间:2015-11-30 14:10:01

标签: python xml xml-parsing lxml elementtree

我有一些XML,如下所示:

<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>

使用Python,我想删除item1item5之间的所有项目,而不必明确命名,以获得以下结果:

<FirstLevel>
    <item1>Val1</item1>
    <item5>Val5</item5>
</FirstLevel>

使用lxml,我知道如何找到item1item5所以我只需要知道如何构建这两者之间的某种XML元素列表。

2 个答案:

答案 0 :(得分:3)

您可以使用preceding-siblingfollowing-sibling的组合,例如:

from lxml.etree import fromstring, tostring

data = """<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>
"""

tree = fromstring(data)
node_start = "item1"
node_end = "item5"

parent = tree.xpath("//FirstLevel")[0]
for node in parent.xpath("*[preceding-sibling::%s and following-sibling::%s]" % (node_start, node_end)):
    parent.remove(node)

print(tostring(tree))

打印:

<FirstLevel>
    <item1>Val1</item1>
    <item5>Val5</item5>
</FirstLevel>

如果您可以在单个节点中多次出现item1item5

item_start = "item1"
item_end = "item5"

parent = tree.xpath("//FirstLevel")[0]
for node_start in parent.xpath("%s" % item_start):
    for node in node_start.xpath("following-sibling::%s" % item_end):
        parent.remove(node)

答案 1 :(得分:1)

感谢alecxe,我找到了解决方案。如果我们有多个item1-item5元素的实例(请参阅我对他的答案的评论以便更好地了解),他的回答对于所描述的案例非常有效,但是没有用(即使是他的更新)。

无论如何,我找到了另一个解决方案(我相信它更简单,更Pythonic):

from lxml.etree import fromstring, tostring

data = """<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>
"""

tree = fromstring(data)

item1_list = tree.findall("item1")

for item1 in item1_list:
    next_node = item1.getnext()
    while next_node.tag != "item5":
        tree.remove(next_node)
        next_node = item1.getnext()

print(tostring(tree))

还有来自alecxe评论的解决方案对我有用:

来自lxml.etree import fromstring,tostring

data = """<FirstLevel>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
    <item1>Val1</item1>
    <item2>Val2</item2>
    <item3>Val3</item3>
    <item4>Val4</item4>
    <item5>Val5</item5>
</FirstLevel>
"""

tree = fromstring(data)
node_start = "item1"
node_end = "item5"

parent = tree.xpath("//FirstLevel")[0]
# Remove first section
for node in parent.xpath("*[(preceding-sibling::item1)[1] and (following-sibling::item5)[3]]"):
    parent.remove(node)
# Remove second section
for node in parent.xpath("*[(preceding-sibling::item1)[2] and (following-sibling::item5)[2]]"):
    parent.remove(node)
# Remove last section
for node in parent.xpath("*[(preceding-sibling::item1)[3] and (following-sibling::item5)[last()]]"):
    parent.remove(node)

print(tostring(tree))

我通过尝试多个值找到了放入preceding-following-sibling的正确索引,但仍然没有真正得到它背后的逻辑,但它至少对我有用。