是否可以使用lxml将文本视为xml元素?

时间:2019-12-27 00:00:18

标签: python xml lxml

我想过滤元素树以删除重复的元素条目。简而言之,我正在尝试将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]这样,我可以轻松地上下步行以减少多余的元素。

1 个答案:

答案 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>