用Python转义XML中的未转义字符

7 投票
4 回答
17431 浏览
提问于 2025-04-16 11:39

我需要处理一个大约5000行的无效XML文件,里面有一些特殊字符需要转义。这里有一个我需要处理的XML示例:

<root>
 <element>
  <name>name & surname</name>
  <mail>name@name.org</mail>
 </element>
</root>

问题在于名字中的字符"&"。我该如何使用Python库来转义这样的特殊字符呢?我在BeautifulSoup中没有找到解决办法。

4 个回答

0

这个回答提供了一些XML清理的功能,不过它们并不是把未转义的字符处理成安全的形式,而是直接把这些字符丢掉了。

使用bs4和lxml

这个问题是想知道如何用Beautiful Soup来处理。这里有一个函数,可以用来清理一个小的XML bytes对象。这个函数是在需要的包版本 beautifulsoup4==4.8.0lxml==4.4.0 下测试过的。需要注意的是,这里使用 bs4 需要依赖 lxml

import xml.etree.ElementTree

import bs4


def sanitize_xml(content: bytes) -> bytes:
    # Ref: https://stackoverflow.com/a/57450722/
    try:
        xml.etree.ElementTree.fromstring(content)
    except xml.etree.ElementTree.ParseError:
        return bs4.BeautifulSoup(content, features='lxml-xml').encode()
    return content  # already valid XML

仅使用lxml

显然,如果只用 lxml 就能完成的事情,使用 bs4lxml 一起就没什么意义了。这个使用清理功能的 lxml==4.4.0 是基本上来源于 jfs的回答

import lxml.etree


def sanitize_xml(content: bytes) -> bytes:
    # Ref: https://stackoverflow.com/a/57450722/
    try:
        lxml.etree.fromstring(content)
    except lxml.etree.XMLSyntaxError:
        root = lxml.etree.fromstring(content, parser=lxml.etree.XMLParser(recover=True))
        return lxml.etree.tostring(root)
    return content  # already valid XML
1

你可能只是想在把HTML放进BeautifulSoup之前,先对它做一些简单的正则表达式处理。

其实更简单的是,如果代码里没有SGML实体(像&...;这样的),你可以用html=html.replace('&','&amp;')来解决这个问题。

如果有的话,可以试试这个:

x ="<html><h1>Fish & Chips & Gravy</h1><p>Fish &amp; Chips &#x0026; Gravy</p>"
import re
q=re.sub(r'&([^a-zA-Z#])',r'&amp;\1',x)
print q

基本上,这个正则表达式是用来查找&后面没有跟字母、数字或#字符的情况。它不会处理行末的&符号,但这个问题应该可以解决。

10

如果你不在乎 XML 中的无效字符,可以使用 XML 解析器的 recover 选项(详细内容可以查看 使用 lxml.etree.iterparse 解析损坏的 XML):

from lxml import etree

parser = etree.XMLParser(recover=True) # recover from bad characters.
root = etree.fromstring(broken_xml, parser=parser)
print etree.tostring(root)

输出

<root>
<element>
<name>name  surname</name>
<mail>name@name.org</mail>
</element>
</root>

撰写回答