2011-08-30 84 views
17

我有一組超級簡單的XML文件來解析...但是...他們使用自定義的實體。我不需要將這些映射到角色,但我希望解析並針對每個角色進行操作。例如:Python ElementTree支持解析未知的XML實體嗎?

<Style name="admin-5678"> 
    <Rule> 
     <Filter>[admin_level]='5'</Filter> 
     &maxscale_zoom11; 
    </Rule> 
</Style> 

有一個在http://effbot.org/elementtree/elementtree-xmlparser.htm一個誘人的暗示,XMLParser的有限實體的支持,但我找不到提到的方法,一切都給人錯誤:這取決於你如何

#!/usr/bin/python 
    ## 
    ## Where's the entity support as documented at: 
    ## http://effbot.org/elementtree/elementtree-xmlparser.htm 
    ## In Python 2.7.1+ ? 
    ## 
    from pprint  import pprint 
    from xml.etree import ElementTree 
    from cStringIO import StringIO 

    parser = ElementTree.ElementTree() 
    #parser.entity["maxscale_zoom11"] = unichr(160) 
    testf = StringIO('<foo>&maxscale_zoom11;</foo>') 
    tree = parser.parse(testf) 
    #tree = parser.parse(testf,"XMLParser") 
    for node in tree.iter('foo'): 
     print node.text 

調整的意見,得出:

xml.etree.ElementTree.ParseError: undefined entity: line 1, column 5 

AttributeError: 'ElementTree' object has no attribute 'entity' 

AttributeError: 'str' object has no attribute 'feed'   

對於那些好奇的XML是從OpenStreetMap的Mapnik的項目。

+0

可能相關的問題:http://stackoverflow.com/questions/2524299/entity-references-and-lxml – unutbu

+0

沒有關係的,因爲在這種情況下,實體實際上是定義。刪除實體定義,你回到我的問題。 – Bryce

+0

fyi - 有人可能希望將/ usr/bin/python修復爲/ usr/bin/env python,因爲大多數系統的shebang行都是錯誤的。 –

回答

11

我不確定這是ElementTree中的一個錯誤還是什麼,但是您需要在expat解析器上調用UseForeignDTD(True)以表現它過去的行爲。

這是一個有點哈克,但你可以通過創建自己的ElementTree.Parser實例,調用它的方法是xml.parsers.expat的實例,然後將它傳遞給ElementTree.parse()做到這一點:

from xml.etree import ElementTree 
from cStringIO import StringIO 


testf = StringIO('<foo>&moo_1;</foo>') 

parser = ElementTree.XMLParser() 
parser.parser.UseForeignDTD(True) 
parser.entity['moo_1'] = 'MOOOOO' 

etree = ElementTree.ElementTree() 

tree = etree.parse(testf, parser=parser) 

for node in tree.iter('foo'): 
    print node.text 

此輸出 「MOOOOO」

或者使用映射接口:

from xml.etree import ElementTree 
from cStringIO import StringIO 

class AllEntities: 
    def __getitem__(self, key): 
     #key is your entity, you can do whatever you want with it here 
     return key 

testf = StringIO('<foo>&moo_1;</foo>') 

parser = ElementTree.XMLParser() 
parser.parser.UseForeignDTD(True) 
parser.entity = AllEntities() 

etree = ElementTree.ElementTree() 

tree = etree.parse(testf, parser=parser) 

for node in tree.iter('foo'): 
    print node.text 

此輸出 「moo_1」

一個更復雜的修復將是子類ElementTree.XMLParser並修復它。

+0

像你說的那樣有點icky,但是謝謝。有沒有辦法避免必須預先定義實體(例如&moo_2)。 – Bryce

+0

@Bryce:被預先定義的是實體的要點,不是嗎?不過:你可以將'parser.entity'設置爲你自己的字典類對象。作爲一個簡單的例子,你可以做'parser.entity = collections。defaultdict(str)'將所有未定義的實體替換爲空字符串。 – Steven

+0

要追蹤@ Steven的評論,你也可以實現一個映射界面,並用鍵來做任何你想做的事情。我編輯了我的答案來展示一個簡單的例子。 – cnelson

3

正如@cnelson已經在評論中指出,這裏選擇的解決方案將無法工作在Python 3

我終於得到了它的工作。引自此Q&A

this post的啓發,我們可以將一些XML定義添加到傳入的原始HTML內容中,然後ElementTree將開箱即用。

這適用於Python 2.6,2.7,3.3,3.4。

import xml.etree.ElementTree as ET 

html = '''<html> 
    <div>Some reasonably well-formed HTML content.</div> 
    <form action="login"> 
    <input name="foo" value="bar"/> 
    <input name="username"/><input name="password"/> 

    <div>It is not unusual to see &nbsp; in an HTML page.</div> 

    </form></html>''' 

magic = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [ 
      <!ENTITY nbsp ' '> 
      ]>''' # You can define more entities here, if needed 

et = ET.fromstring(magic + html)