首页 > 解决方案 > 是否可以使用 lxml 将文本视为 xml 元素?

问题描述

我想过滤元素树以删除重复的元素条目。简而言之,我正在尝试将 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包含ul. 它会像想的那样返回一个列表[ul, p],我想要一个包含以下内容的列表:

[Text, Ul, P, Text]所以我可以轻松地在树上走下或上走,以减少多余的元素。

标签: pythonxmllxml

解决方案


lxml 的文档表明它们没有文本节点,并且该文本将是通过 .text 访问的该元素的一部分,或者将是通过 .tail 访问的结束标记的尾部。

<html><body>Hello<br/>World</body></html>

在这里,<br/>标签被文本包围。这通常称为文档样式或混合内容 XML。元素通过其 tail 属性支持这一点。它包含直接跟随元素的文本,直到 XML 树中的下一个元素。

.text 和 .tail 这两个属性足以表示 XML 文档中的任何文本内容。这样,除了 Element 类之外,ElementTree API 不需要任何特殊的文本节点,这些节点往往会经常妨碍(正如您可能从经典的 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  ']

所以“Text node 1”就是主p的文本。但是“文本节点 3”,而它在主 p 内部实际上是内部 p 的尾标记。

除此之外,您可以迭代主元素,如果子元素是 ap 标签,您可以将其移出主 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>

推荐阅读