![](/img/trans.png)
[英]python etree parse xml with html entities ( keep html formatting )
[英]Parse XML with (X)HTML entities
嘗試使用ElementTree解析包含未定義實體(即
)的XML:
ParseError: undefined entity
在Python 2.x中,可以通過創建解析器( 文檔 )來更新XML實體dict:
parser = ET.XMLParser()
parser.entity["nbsp"] = unichr(160)
但是如何用Python 3.x做同樣的事情呢?
更新:我的方面存在誤解,因為我忽略了我在嘗試更新XML實體dict之前調用了parser.parser.UseForeignDTD(1)
,這導致了解析器的錯誤。 幸運的是,@ m.brindley耐心地指出,XML實體dict仍然存在於Python 3.x中,並且可以像在Python 2.x中那樣進行更新。
這里的問題是XML中唯一有效的助記符實體是quot
, amp
, apos
, lt
和gt
。 這意味着幾乎所有(X)HTML命名實體都必須使用XML 1.1規范中定義的實體聲明標記在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參數中,有一個“ 預定義HTML實體 ”的空間,但該參數尚未實現。 在init方法中創建一個名為entity
的空dict,這是用於查找未定義實體的內容。
我不認為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語法。
解決方案:
chr()
- unichr()
不再是有效的名字
html.entities.html5
更新XMLParser.entity
,以將所有有效的HTML5助記符實體映射到其字符。 HTMLParser
子類HTMLParser
處理助記符實體,但這不會根據需要返回ElementTree
。 這是我使用的代碼片段 - 它通過HTMLParser
解析XML與外部DOCTYPE(通過子類化演示如何添加實體處理), ET.XMLParser
通過實體映射和expat
ET.XMLParser
(由於外部DOCTYPE,它將默默地忽略未定義的實體) 。 有一個有效的XML實體( >
)和一個未定義的實體( ©
),我用ET.XMLParser
映射到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)
我遇到了類似的問題,並使用lxml解決了這個問題。 它的etree.XMLParser
有一個recover
關鍵字參數,迫使它試圖忽略損壞的XML。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.