Поддержка Python ElementTree для анализа неизвестных объектов XML? - PullRequest
19 голосов
/ 30 августа 2011

У меня есть набор очень простых 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.

Ответы [ 2 ]

12 голосов
/ 01 сентября 2011

Я не уверен, является ли это ошибкой в ​​ElementTree или чем-то, но вам нужно вызвать 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 и исправить его там.

8 голосов
/ 24 февраля 2016

Как уже отметил @cnelson в комментарии, выбранное здесь решение не будет работать в Python 3.

Я наконец-то получил его на работу.Цитируется из этого Q & A .

Вдохновленный этим сообщением , мы можем просто добавить некоторое определение 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)
...