我想过滤元素树以删除重复的元素条目。简而言之,我正在尝试将xml输出清除为可以由其他工具解析的内容。
例如
<p>
<p>
Text node 1
<ul>
<li>asdasd</li>
</ul>
<p>
Text node 2 <span>Som text</span>
</p>
Text node 3
</p>
<p>Text node 4</p>
</p>
将被转换为此:
<p>
Text node 1
<ul>
<li>asdasd</li>
</ul>
</p>
<p>Text node 2 <span>Som text</span></p>
<p>Text node 3</p>
<p>Text node 4</p>
在lxml中,getchildren
似乎只返回xml元素。因此,当我在包含p
的{{1}}上调用getchildren时。它将返回一个像ul
一样的列表,我想要一个包含以下内容的列表:
[ul, p]
这样,我可以轻松地上下步行以减少多余的元素。
答案 0 :(得分:2)
lxml的文档建议它们没有文本节点,并且该文本要么是通过.text访问的元素的一部分,要么是通过.tail访问的结束标记的结尾。
<html><body>Hello<br/>World</body></html>
在这里,
<br/>
标签是 被文字包围。这通常称为文档样式或 混合内容XML。元素通过其tail属性支持这一点。 它包含紧随元素之后直到下一个元素的文本。 XML树中的元素。.text和.tail这两个属性足以表示任何文本 XML文档中的内容。这样,ElementTree API不会 除了Element类之外,还需要任何特殊的文本节点, 往往会经常妨碍您(如您可能从经典 DOM API)。
我不能说以下是您所要的东西,但至少可以使您走近一步。
from lxml import etree
tree = etree.parse("test.dat").getroot()
main_p = tree[0]
elements = [main_p.text]
for child in main_p:
elements.append(child.tag)
elements.append(child.tail)
print(f"TAG: {child.tag} has tail: #{child.tail}#")
print(elements)
输出
TAG: ul has tail: #
#
TAG: p has tail: #
Text node 3
#
['\n Text node 1\n ', 'ul', '\n ', 'p', '\n Text node 3\n ']
因此,“文本节点1”是主p的文本。但是“文本节点3”实际上位于内部p的尾部标记中。
除此之外,您还可以迭代主元素,如果子元素是p标签,则可以将其移出主p并将其添加到根标签中。再次在下面只是一个例子。
from lxml import etree
tree = etree.parse("test.dat").getroot()
main_p = tree[0]
elements = [main_p.text]
for child in main_p[::-1]:
if child.tag == 'p':
tree.insert(tree.index(main_p) + 1, child)
new_p = etree.Element('p')
new_p.text = child.tail
tree.insert(tree.index(child)+1, new_p)
child.tail = "\n"
tree.tag = 'something_else'
print(etree.tostring(tree, pretty_print=True).decode('utf-8'))
输出
<something_else>
<p>
Text node 1
<ul>
<li>asdasd</li>
</ul>
</p>
<p>
Text node 2
<span>Som text</span>
</p>
<p>Text node 3</p>
<p>Text node 4</p>
</something_else>