首页 > 解决方案 > 如何解析 XML 将特定标签或其内容保留为纯文本?

问题描述

我正在解析一个 XML 格式的文档,它支持一些类似 HTML 的标签,包括内联格式标签。我想以正常方式解析所有标签,但不解析标签内容,p以便我可以以结构感知方式处理结构化数据,并回退到文本内容和格式的纯字符串操作。例如

<root>
    <data1><field1>qwerty</field1> <field2>qwerty</field2> </data1>
    <body>
        <p>Lorem ipsum</p>
        <p>Dolor <a href="http://example.com">sit</a> <em><strong>amet</strong> consectetuer</em></p>
    </body>
</root>

应该产生一个树,其中根元素包含一个 data1 元素和一个 body 元素;data1 元素包含 field1 和 field2 元素(这些标签后面的空格应该被丢弃);body 元素包含 2 个 p 元素,但 p 元素(全部)不包含子标签,并将其全部内容公开为文本字符串。

即给定第二个 p 标签对象,我希望以下内容为真:

p.text == 'Dolor <a href="http://example.com">sit</a> <em><strong>amet</strong> consectetuer'

是否要通过“将 ap 标签中包含的所有内容视为纯文本”或“将 a、em 和强标签视为纯文本,无论它们在哪里”来实现这一点并不重要,尽管我觉得有兴趣了解这两种方式。

事实上,提到的标签并不是唯一相关的,我需要能够指定该原则将适用的标签的名称。

如何在 Python 中以这种方式解析 XML?

使用哪个 XML 库对我来说并不重要。我以前没有在 Python 中使用过 XML,到目前为止也没有选择过。我现在正在使用 lxml(因为我已经阅读过它可以自动解码 ASCII 文件并且具有更好的 XPath 支持,它也不会像 ElementTree 那样混淆命名空间快捷方式)但是其中有很多(ElementTree、MiniDOM、lxml 、Untangle、BeautifulSoup 等)我真的不知道哪个更适合我。顺便说一句,谁知道 Python XML 库的一个很好的比较概述,如果您能在评论中分享指向它的链接,我将不胜感激。

标签: pythonxml

解决方案


这里

import xml.etree.ElementTree as ET

xml = '''<root>
    <data1><field1>qwerty</field1> <field2>qwerty</field2> </data1>
    <body>
        <p>Lorem ipsum</p>
        <p>Dolor <a href="http://example.com">sit</a> <em><strong>amet</strong> consectetuer</em></p>
    </body>
</root>'''

TEST_STRINGS = ['<p>Lorem ipsum</p>',
                '<p>Dolor <a href="http://example.com">sit</a> <em><strong>amet</strong> consectetuer</em></p>']


def _add_text_before_traversing_tree(e):
    return e.tag in ['p']


def _handle_attrib(attrib):
    result = ''
    for k, v in attrib.items():
        result += '{}="{}"'.format(k, v)
    return ' ' + result if result else ''


def _element_to_text(e, tree_fragments):
    tree_fragments.append('<{}'.format(e.tag))
    if not e.attrib:
        tree_fragments.append('>')
    tree_fragments.append(_handle_attrib(e.attrib))
    add_text_now = _add_text_before_traversing_tree(e)
    if add_text_now:
        tree_fragments.append(e.text if e.text else '')
    for child in list(e):
        _element_to_text(child, tree_fragments)
    if not add_text_now:
        if not e.attrib:
            tree_fragments.append(e.text if e.text else '')
        else:
            tree_fragments.append('>' + e.text if e.text else '')
    tree_fragments.append('</{}>'.format(e.tag))
    tree_fragments.append(e.tail.strip() if e.tail else '')


def element_to_text(e):
    """ Traverse element tree and return a string representation of the tree"""
    tree_fragments = []
    _element_to_text(e, tree_fragments)
    tree_fragments = [x for x in tree_fragments if len(x) > 0]
    return ''.join(tree_fragments)


root = ET.fromstring(xml)
p_elements = root.findall('./body/p')
for idx, p in enumerate(p_elements):
    element_as_text = element_to_text(p)
    print('Original text  : ' + TEST_STRINGS[idx])
    print('Element as text: ' + element_as_text)
    print('')
    # now you need to create a new element,
    # attach it to the parent element ('body'),
    # set its new text and remove the current element

输出

Original text  : <p>Lorem ipsum</p>
Element as text: <p>Lorem ipsum</p>

Original text  : <p>Dolor <a href="http://example.com">sit</a> <em><strong>amet</strong> consectetuer</em></p>
Element as text: <p>Dolor <a href="http://example.com">sit</a><em><strong>amet</strong>consectetuer</em></p>

推荐阅读