Поддержка Python ElementTree для анализа неизвестных объектов XML?
У меня есть набор очень простых файлов 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 взят из проекта mapnik OpenStreetMap.
2 ответа
Я не уверен, является ли это ошибкой в 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
Это выводит "МООООО"
Или используя интерфейс отображения:
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 и исправить его там.
Как @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 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)