解析XHTML5未定义实体(Parse XHTML5 with undefined entities

2019-10-20 09:20发布

请考虑一下:

import xml.etree.ElementTree as ET

xhtml = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
        <html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
        <head><title>XHTML sample</title></head>
            <body>
                <p>&nbsp;Sample text</p>
            </body>
        </html>
'''
parser = ET.XMLParser()
parser.entity['nbsp'] = '&#x00A0;'
tree = ET.fromstring(xhtml, parser=parser)
print(ET.tostring(tree, method='xml'))

这呈现漂亮的文本表示xhtml字符串。

但是,对于HTML5文档类型相同的XHTML文档:

xhtml = '''<!DOCTYPE html>
        <html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
        <head><title>XHTML sample</title></head>
            <body>
                <p>&nbsp;Sample text</p>
            </body>
        </html>
'''

我得到异常:

xml.etree.ElementTree.ParseError: undefined entity: line 5, column 19

所以解析器不能处理它,虽然我加nbsp到实体字典。

同样的情况,如果我使用lxml

from lxml import etree
parser = etree.XMLParser(resolve_entities=False)
tree = etree.fromstring(xhtml, parser=parser)
print etree.tostring(tree, method='xml')

提出:

lxml.etree.XMLSyntaxError: Entity 'nbsp' not defined, line 5, column 26

虽然我已经设置解析器忽略实体。

这是为什么,以及如何使的XHTML文件解析与可能HTML5 doctype声明?


对于LXML部分解决方案是使用回收器:

parser = etree.XMLParser(resolve_entities=False, recover=True)

但我仍然在等待更好的。

Answer 1:

这里的问题是,该Expat解析器在后台使用一般不会主动报告未知的实体-它宁愿抛出一个错误,所以在回退代码xml.etree.ElementTree你试图触发将不能运行。 您可以使用UseForeignDTD方法来改变这种行为,就会使外籍人士忽略的doctype声明,并通过所有实体声明到xml.etree.ElementTree 。 下面的代码工作正常:

import xml.etree.ElementTree as ET

xhtml = '''<!DOCTYPE html>
        <html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
        <head><title>XHTML sample</title></head>
            <body>
                <p>&nbsp;Sample text</p>
            </body>
        </html>
'''
parser = ET.XMLParser()
parser._parser.UseForeignDTD(True)
parser.entity['nbsp'] = u'\u00A0'
tree = ET.fromstring(xhtml, parser=parser)
print(ET.tostring(tree, method='xml'))

这种方法的副作用:正如我所说,DOCTYPE声明完全被忽略。 这意味着你必须声明所有实体,甚至被DOCTYPE理应覆盖的人。

请注意,您投入值ElementTree.XMLParser.entity字典必须定期字符串,文本,该实体将被替换-你不能再参考其他实体存在。 所以它应该是u'\u00A0'&nbsp;



文章来源: Parse XHTML5 with undefined entities