ここでの問題は、XMLの唯一の有効なニーモニックエンティティquot
,amp
,apos
,lt
およびgt
である。つまり、XML 1.1 specで定義されたentity declaration markupを使用して、ほとんどすべて(X)のHTML名前付きエンティティをDTDで定義する必要があります。文書がスタンドアロンにする場合、これがそうのようなインラインDTDで行われるべきである:
<?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>
xml.etree.ElementTree
でXMLParser
は、実際の解析を行うためにxml.parsers.expat
を使用しています。 XMLParser
のinit引数には、 'predefined HTML entities'のためのスペースがありますが、その引数はまだ実装されていません。 entity
という名前の空の辞書がinitメソッドで作成され、これが未定義のエンティティを検索するために使用されます。
expat(拡張子はET XMLParser)は、これを回避するために名前空間をXHMTLなどに切り替えることはできません。おそらく、それは外部の名前空間の定義を取得しないためです(私はxmlns="http://www.w3.org/1999/xhtml"
をデータ要素のデフォルトの名前空間にしようとしましたが、うまくいきませんでした)が、それを確認することはできません。デフォルトでは、expatはXML以外のエンティティに対してエラーを発生させますが、外部のDOCTYPEを定義することで回避できます。これにより、expatパーサーは未定義のエンティティエントリをET.XMLParser
の_default()
メソッドに戻します。
_default()
メソッドは、XMLParser
インスタンスでentity
dictを検索し、一致するキーが見つかると、関連する値でエンティティを置き換えます。これは、質問に記載されているPython-2.xの構文を維持します。
ソリューション:
- データが外部のDOCTYPEを持っているし、(X)HTMLニーモニック実体を持っていない場合、あなたは運外です。それは有効なXMLではなく、expatはエラーをスローするのが正しいです。外部DOCTYPEを追加する必要があります。
- データに外部DOCTYPEがある場合は、古い構文を使用してニーモニック名を文字にマップできます。 注:py3kで
chr()
を使用する必要があります - unichr()
はもう別
- 有効な名前ではありません、あなたはそれらの文字にすべての有効なHTML5のニーモニックエンティティをマッピングするために
html.entities.html5
でXMLParser.entity
を更新することができます。
- データがXHTMLの場合は、ニーモニックエンティティを処理するために
HTMLParser
をサブクラス化することができますが、必要に応じて、これはElementTree
を返しません。ここで
私が使用したスニペットである - それはHTMLParser
(サブクラス化によるエンティティの処理を追加する方法を示すために)、エンティティのマッピングとET.XMLParser
とexpat
を介して外部DOCTYPEでXMLをパース(ただ黙っによる未定義の実体を無視しますました外部DOCTYPE)。有効なXMLエンティティ(>
)と未定義のエンティティ(©
)があり、これはでchr(0x24B4)
にマップします。
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)
問題を調査していただきありがとうございますが、私はPython 3.x SPLがXMLエンティティテーブルの更新を許可していないと疑っています。少なくとも私はそのような発表を見つけることができませんでした。申し訳ありませんが、正規表現を使用してリモートのXHTMLデータを作成することは考えられません。 – theta
私はこれにもう少し時間をかけて、expatが 'XMLParser'の' _default() 'メソッドに渡っていない理由を試しました。私の編集を参照してください - 外部DOCTYPEが定義されている場合、マップエンティティが可能です。 –
私はこの質問をより一般的なものにしたいと思っていましたが、問題をもっと「局所的に」フィルタリングしてみましょう:XHTMLデータに 'xhtml1-transitional.dtd'があり、定義されていないXMLエンティティだけが' 'です。私はデフォルトでlxmlを使用し、使用できない場合はSPLにフォールバックしますが、ETを返します。 – theta