ここでの問題は、XMLで有効なニーモニックエンティティが、、、、quot
およびでamp
あるということです。つまり、ほとんどすべての(X)HTML名前付きエンティティは、 XML 1.1仕様で定義されているエンティティ宣言マークアップを使用して、DTDで定義する必要があります。ドキュメントをスタンドアロンにする場合は、次のようにインラインDTDを使用して実行する必要があります。apos
lt
gt
<?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>
XMLParser
inxml.etree.ElementTree
は、を使用xml.parsers.expat
して実際の解析を行います。のinit引数には、「事前定義されたHTMLエンティティXMLParser
」用のスペースがありますが、その引数はまだ実装されていません。名前付きの空のdictがinitメソッドで作成され、これが未定義のエンティティを検索するために使用されます。entity
expat(拡張により、ET XMLParser)は、これを回避するために名前空間をXHMTLのようなものに切り替えることを処理できるとは思いません。おそらく、外部の名前空間定義をフェッチしないためです(xmlns="http://www.w3.org/1999/xhtml"
データ要素のデフォルトの名前空間を作成しようとしましたが、うまく機能しませんでした)が、それを確認できません。デフォルトでは、expatは非XMLエンティティに対してエラーを発生させますが、外部DOCTYPEを定義することでこれを回避できます。これにより、expatパーサーは未定義のエンティティエントリをET.XMLParser
の_default()
メソッドに返します。
この_default()
メソッドはentity
、インスタンス内のdictをXMLParser
検索し、一致するキーが見つかった場合、エンティティを関連する値に置き換えます。これにより、質問で言及されているPython-2.x構文が維持されます。
ソリューション:
- データに外部DOCTYPEがなく、(X)HTMLニーモニックエンティティがある場合は、運が悪いです。これは有効なXMLではなく、expatはエラーをスローする権利があります。外部DOCTYPEを追加する必要があります。
- データに外部DOCTYPEがある場合は、古い構文を使用してニーモニック名を文字にマップできます。注:
chr()
py3kで使用する必要があります-unichr()
有効な名前ではなくなりました
XMLParser.entity
または、で更新しhtml.entities.html5
て、すべての有効なHTML5ニーモニックエンティティをそれらの文字にマップすることもできます。
HTMLParser
データがXHTMLの場合、ニーモニックエンティティを処理するためにサブクラス化できますが、これはElementTree
希望どおりに返されません。
これが私が使用したスニペットです-外部DOCTYPEを介してHTMLParser
(サブクラス化によってエンティティ処理を追加する方法を示すために)、ET.XMLParser
エンティティマッピングを使用してXMLを解析します(expat
外部DOCTYPEのために未定義のエンティティを黙って無視します)。でマップする有効な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)