15

未定義のエンティティ (つまり ) を含む XML を ElementTree で解析しようとすると、次のエラーが発生します。

ParseError: undefined entity  

Python 2.x では、パーサーを作成することで XML エンティティ dict を更新できます (ドキュメント):

parser = ET.XMLParser()
parser.entity["nbsp"] = unichr(160)

しかし、Python 3.x で同じことを行うにはどうすればよいでしょうか?


parser.parser.UseForeignDTD(1)更新: XML エンティティ dict を更新しようとする前に呼び出していたことを見落としたため、パーサーでエラーが発生していたため、私の側から誤解がありました。幸いなことに、@m.brindley は辛抱強く、Python 3.x に XML エンティティ dict がまだ存在し、Python 2.x と同じ方法で更新できることを指摘しました。

4

3 に答える 3

18

ここでの問題は、XMLで有効なニーモニックエンティティが、、、、quotおよびでampあるということです。つまり、ほとんどすべての(X)HTML名前付きエンティティは、 XML 1.1仕様で定義されているエンティティ宣言マークアップを使用して、DTDで定義する必要があります。ドキュメントをスタンドアロンにする場合は、次のようにインラインDTDを使用して実行する必要があります。aposltgt

<?xml version="1.1" ?>
<!DOCTYPE naughtyxml [
    <!ENTITY nbsp "&#0160;">
    <!ENTITY copy "&#0169;">
]>
<data>
    <country name="Liechtenstein">
        <rank>1&nbsp;&gt;</rank>
        <year>2008&copy;</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
</data>

XMLParserinxml.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エンティティ(&gt;)と未定義のエンティティ( )があります。&copy;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&gt;</rank>
        <year>2008&copy;</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)
于 2013-02-09T09:03:47.847 に答える