如何在lxml中处理XML文档中的 类似实体?

12 投票
3 回答
15595 浏览
提问于 2025-04-16 12:50

考虑以下内容:

from lxml import etree
from StringIO import StringIO

x = """<?xml version="1.0" encoding="utf-8"?>\n<aa>&nbsp;&acirc;</aa>"""
p = etree.XMLParser(remove_blank_text=True, resolve_entities=False)
r = etree.parse(StringIO(x), p)

这段代码会出现错误,错误信息是:
lxml.etree.XMLSyntaxError: Entity 'nbsp' not defined, line 2, column 11

出现这个错误是因为 resolve_entities=False 并不是忽略这些实体,它只是没有处理它们。

如果我使用 etree.HTMLParser,它会自动创建 htmlbody 标签,还会进行很多其他特殊处理,以适应 HTML

那么,使用 lxml 的话,怎样才能在 aa 标签下获取一个 &nbsp;&acirc; 的文本子节点呢?

3 个回答

-1

当我尝试做类似的事情时,我只是用了 x.replace('&', '&amp;') 在解析字符串之前。

7

@Alex说得对:你的文档不是格式正确的XML,所以XML解析器无法解析它。一个解决办法是先处理一下文档的文本,把那些不合法的实体替换成它们对应的utf-8字符:

entities = [
    ('&nbsp;', u'\u00a0'),
    ('&acirc;', u'\u00e2'),
    ...
    ]

for before, after in entities:
    x = x.replace(before, after.encode('utf8'))

当然,如果你的“xml”格式非常奇怪,这个方法也可能会失效。

最好的办法还是修正你的输入XML文档,使其成为格式正确的XML。

13

你不能忽视实体,因为它们是XML定义的一部分。如果你的文档没有DTD(文档类型定义)或者没有设置standalone="yes",或者包含了没有在DTD中定义的实体,那么你的文档就不符合规范。你不能假装你的文档是HTML。

https://mailman-mail5.webfaction.com/pipermail/lxml/2008-February/003398.html

你可以试着假装一下,把一个XHTML的DTD放到你的文档里。例如:

from lxml import etree
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO
x = """<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >\n<aa>&nbsp;&acirc;</aa>"""
p = etree.XMLParser(remove_blank_text=True, resolve_entities=False)
r = etree.parse(StringIO(x), p)
etree.tostring(r) # '<aa>&nbsp;&acirc;</aa>'

撰写回答