Python ElementTree支持解析未知的XML实体吗?

问题描述:

我有一组超级简单的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://*.com/questions/2524299/entity-references-and-lxml – unutbu

+0

没有关系的,因为在这种情况下,实体实际上是定义。删除实体定义,你回到我的问题。 – Bryce

+0

fyi - 有人可能希望将/ usr/bin/python修复为/ usr/bin/env python,因为大多数系统的shebang行都是错误的。 –

我不确定这是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

正如@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)