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

Bry*_*yce 20 python xml openstreetmap

我有一组超级简单的XML文件来解析......但是......他们使用自定义的实体.我不需要将这些映射到字符,但我确实希望对每个字符进行解析和操作.例如:

<Style name="admin-5678">
    <Rule>
      <Filter>[admin_level]='5'</Filter>
      &maxscale_zoom11;
    </Rule>
</Style>
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这取决于您如何调整评论:

xml.etree.ElementTree.ParseError: undefined entity: line 1, column 5
Run Code Online (Sandbox Code Playgroud)

要么

AttributeError: 'ElementTree' object has no attribute 'entity'
Run Code Online (Sandbox Code Playgroud)

要么

AttributeError: 'str' object has no attribute 'feed'           
Run Code Online (Sandbox Code Playgroud)

对于那些好奇的人来说,XML来自OpenStreetMap的mapnik项目.

cne*_*son 13

我不确定这是否是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
Run Code Online (Sandbox Code Playgroud)

这输出"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
Run Code Online (Sandbox Code Playgroud)

输出"moo_1"

更复杂的修复方法是继承ElementTree.XMLParser并在那里修复它.

  • 我不确定目前在 Python 3 中这是否可行。查看 [docs](http://docs.python.org/3/library/xml.etree.elementtree.html#xmlparser-objects) 我看到以下方法签名 **xml.etree.ElementTree.XMLParser( html=0, target=None, encoding=None)** 但文档说 _Element 结构构建器用于 XML 源数据,基于 expat 解析器。html 是预定义的 HTML 实体。当前实现不支持此标志。_看起来元素树变得越来越严格,如果您的实体未定义,则它无效且不会被解析。 (2认同)

Ray*_*Luo 11

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

我终于搞定了.引用此问答.

这篇文章的启发,我们可以将一些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)
Run Code Online (Sandbox Code Playgroud)