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

23

我有一组非常简单的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项目。


可能相关的问题:https://dev59.com/ikzSa4cB1Zd3GeqPp9EP - unutbu
这与实体已定义不相关。移除实体定义后,我们回到我的问题。 - Bryce
FYI - 有人可能想要将 /usr/bin/python 更改为 /usr/bin/env python,因为 shebang 行对于大多数系统来说是错误的。 - Good Person
2个回答

16

正如@cnelson在评论中指出的那样,这里选择的解决方案在Python 3中不起作用。

我最终让它工作了。引用自此问答

这篇帖子的启发,我们可以在传入的原始HTML内容前加入一些XML定义,然后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)

1
这仅适用于HTML文档,对吧?“DOCTYPE”处理指令的概念不适用于OP明显正在处理的“简单XML文件”。 - Frerich Raabe
@FrerichRaabe,抱歉,我承认我没有在XML文档上进行测试。那个答案是从这里引用的,希望它有所帮助。那个原始的Q&A链接包含另一个答案,可能对你的情况有帮助,也可能没有。 - RayLuo

14

我不确定这是否是ElementTree中的一个错误,但您需要在expat解析器上调用UseForeignDTD(True)才能像以前一样运行。

这有点hacky,但您可以通过创建自己的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并在那里进行修复。


有点棘手,但谢谢。有没有办法避免预定义实体(例如&moo_2)? - Bryce
跟随@Steven的评论,您还可以实现映射接口,并对键执行任何操作。我编辑了我的回答以展示一个简单的例子。 - cnelson
1
在Python 3中使用cpython,这将无法工作,而应改用C版本(以前称为“cElementTree”)。 - phihag
3
目前我不确定在Python 3中是否有可能实现这一点。查看文档后,我发现下面的方法签名 **xml.etree.ElementTree.XMLParser(html=0, target=None, encoding=None)**,但文档上说“基于expat解析器的XML源数据元素结构生成器。html是预定义的HTML实体。当前版本不支持该标志。”看起来ElementTree的要求越来越严格了,如果您的实体未定义,则其将无效并且不会被解析。 - cnelson
1
我曾经在2.7中使用重写的ElementTree XMLParser使其正常工作,但正如你所指出的那样,在3.5中无法继承cElementTree解析器。不确定是否应该在解析之前通过自定义编解码器来推送我的内容,或者还有其他更好的方法。对于Python 3,是否有标准答案? - Epu
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接