这里的问题是 XML 中唯一有效的助记符实体是quot、amp、apos和。这意味着几乎所有 (X)HTML 命名实体都必须使用XML 1.1 规范中定义的实体声明标记在 DTD 中定义。如果文档是独立的,则应使用内联 DTD 来完成,如下所示:ltgt
<?xml version="1.1" ?>
<!DOCTYPE naughtyxml [
    <!ENTITY nbsp " ">
    <!ENTITY copy "©">
]>
<data>
    <country name="Liechtenstein">
        <rank>1 ></rank>
        <year>2008©</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
</data>
XMLParserinxml.etree.ElementTree使用 an来进行实际的xml.parsers.expat解析。在 的 init 参数中,“预定义的 HTML 实体XMLParser”有一个空格,但该参数尚未实现。在 init 方法中创建了一个名为的空字典,它用于查找未定义的实体。entity
我不认为 expat(通过扩展,ET XMLParser)能够处理将命名空间切换到 XHMTL 之类的东西来解决这个问题。可能是因为它不会获取外部命名空间定义(我尝试xmlns="http://www.w3.org/1999/xhtml"为数据元素创建默认命名空间,但效果不佳)但我无法确认。默认情况下,expat 将针对非 XML 实体引发错误,但您可以通过定义外部 DOCTYPE 来解决此问题 - 这会导致 expat 解析器将未定义的实体条目传递回ET.XMLParser'_default()方法。
该_default()方法entity在实例中查找字典,XMLParser如果找到匹配的键,它将用关联的值替换实体。这维护了问题中提到的 Python-2.x 语法。
解决方案:
- 如果数据没有外部 DOCTYPE 并且有 (X)HTML 助记符实体,那么你就不走运了。它不是有效的 XML,expat 抛出错误是正确的。您应该添加一个外部 DOCTYPE。
- 如果数据具有外部 DOCTYPE,您可以使用旧语法将助记符名称映射到字符。注意:您应该chr()在 py3k 中使用 -unichr()不再是有效名称
- 或者,您可以更新XMLParser.entity以html.entities.html5将所有有效的 HTML5 助记符实体映射到它们的字符。
 
- 如果数据是 XHTML,您可以创建子类HTMLParser来处理助记符实体,但这不会返回ElementTree所需的值。
HTMLParser这是我使用的片段 - 它通过(以演示如何通过子类化添加实体处理)、ET.XMLParser实体映射和expat(由于外部 DOCTYPE 将默默地忽略未定义的实体)来解析带有外部 DOCTYPE 的 XML 。有一个有效的 XML 实体 ( >) 和一个未定义的实体 ( ©),我chr(0x24B4)使用ET.XMLParser.
from html.parser import HTMLParser
from html.entities import name2codepoint
import xml.etree.ElementTree as ET
import xml.parsers.expat as expat
xml = '''<?xml version="1.0"?>
<!DOCTYPE data PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<data>
    <country name="Liechtenstein">
        <rank>1></rank>
        <year>2008©</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
</data>'''
# HTMLParser subclass which handles entities
print('=== HTMLParser')
class MyHTMLParser(HTMLParser):
    def handle_starttag(self, name, attrs):
        print('Start element:', name, attrs)
    def handle_endtag(self, name):
        print('End element:', name)
    def handle_data(self, data):
        print('Character data:', repr(data))
    def handle_entityref(self, name):
        self.handle_data(chr(name2codepoint[name]))
htmlparser = MyHTMLParser()
htmlparser.feed(xml)
# ET.XMLParser parse
print('=== XMLParser')
parser = ET.XMLParser()
parser.entity['copy'] = chr(0x24B8)
root = ET.fromstring(xml, parser)
print(ET.tostring(root))
for elem in root:
    print(elem.tag, ' - ', elem.attrib)
    for subelem in elem:
        print(subelem.tag, ' - ', subelem.attrib, ' - ', subelem.text)
# Expat parse
def start_element(name, attrs):
    print('Start element:', name, attrs)
def end_element(name):
    print('End element:', name)
def char_data(data):
    print('Character data:', repr(data))
print('=== Expat')
expatparser = expat.ParserCreate()
expatparser.StartElementHandler = start_element
expatparser.EndElementHandler = end_element
expatparser.CharacterDataHandler = char_data
expatparser.Parse(xml)