Python元素树 - 从元素中提取文本,剥离标签

时间:2013-10-14 21:53:00

标签: python xml-parsing elementtree

使用Python中的ElementTree,如何从节点中提取所有文本,剥离该元素中的任何标记并仅保留文本?

例如,假设我有以下内容:

<tag>
  Some <a>example</a> text
</tag>

我想要返回Some example text。我该怎么做呢?到目前为止,我采取的方法都有相当严重的后果。

3 个答案:

答案 0 :(得分:16)

如果您在Python 3.2+下运行,则可以使用itertext

itertext创建一个文本迭代器,它以文档顺序循环遍历此元素和所有子元素,并返回所有内部文本:

import xml.etree.ElementTree as ET
xml = '<tag>Some <a>example</a> text</tag>'
tree = ET.fromstring(xml)
print(''.join(tree.itertext()))

# -> 'Some example text'

如果您使用的是较低版本的Python,则可以通过将其附加到Element类来重复使用the implementation of itertext(),之后您可以像上面一样调用它:

# original implementation of .itertext() for Python 2.7
def itertext(self):
    tag = self.tag
    if not isinstance(tag, basestring) and tag is not None:
        return
    if self.text:
        yield self.text
    for e in self:
        for s in e.itertext():
            yield s
        if e.tail:
            yield e.tail

# if necessary, monkey-patch the Element class
if 'itertext' not in ET.Element.__dict__:
    ET.Element.itertext = itertext

xml = '<tag>Some <a>example</a> text</tag>'
tree = ET.fromstring(xml)
print(''.join(tree.itertext()))

# -> 'Some example text'

答案 1 :(得分:4)

正如文档所述,如果您只想阅读文本而没有任何中间标记,则必须以正确的顺序递归连接所有texttail属性。

但是,最近足够的版本(包括2.7和3.2中的stdlib,但不是2.6或3.1,以及PyPI上ElementTreelxml的当前发布版本)可以做这是tostring方法中自动生成的:

>>> s = '''<tag>
...   Some <a>example</a> text
... </tag>'''
>>> t = ElementTree.fromstring(s)
>>> ElementTree.tostring(s, method='text')
'\n  Some example text\n'

如果您还想从文本中删除空格,则需要手动执行此操作。在您的简单案例中,这很简单:

>>> ElementTree.tostring(s, method='text').strip()
'Some example text'

但是,在更复杂的情况下,如果要在中间标记中删除空格,则可能不得不依次递归处理texttail。那不是太难;你只需要记住处理属性可能是None的可能性。例如,这是一个骨架,您可以将自己的代码挂钩:

def textify(t):
    s = []
    if t.text:
        s.append(t.text)
    for child in t.getchildren():
        s.extend(textify(child))
    if t.tail:
        s.append(t.tail)
    return ''.join(s)

此版本仅在texttail保证为strNone时有效。对于手动构建的树,不能保证是真的。

答案 2 :(得分:0)

Aslo存在一个非常简单的解决方案,以防可能使用XPath。它称为XPath轴:more about it can be found here

当具有本身包含文本的节点(例如标签div)以及其他包含文本的节点(例如标签acenter或另一个div)时在内部或仅包含文本,并且我们希望选择该div节点中的所有文本,可以通过以下XPath:current_element.xpath("descendant-or-self::*/text()").extract()来实现。我们将得到的是当前元素中所有文本的列表,如果有的话,剥离其中的标签。

它的好处是不需要递归函数,XPath会处理所有这些(使用回弹本身,但对我们而言,它是如此干净)。

Here is StackOverflow question concerning this proposed solution