2011-08-30 31 views
17

Tengo un conjunto de archivos XML super simples para analizar ... pero ... usan entidades definidas a medida. No necesito asignarlos a los personajes, pero sí quiero analizarlos y actuar en cada uno. Por ejemplo:Compatibilidad con Python ElementTree para analizar entidades XML desconocidas

<Style name="admin-5678"> 
    <Rule> 
     <Filter>[admin_level]='5'</Filter> 
     &maxscale_zoom11; 
    </Rule> 
</Style> 

Hay una tentadora pista en http://effbot.org/elementtree/elementtree-xmlparser.htm que XMLParser ha limitado el apoyo entidad, pero no puede encontrar los métodos mencionados, todo lo que da errores:

#!/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 

que, dependiendo de cómo se ajustar los comentarios da:

xml.etree.ElementTree.ParseError: undefined entity: line 1, column 5 

o

AttributeError: 'ElementTree' object has no attribute 'entity' 

o

AttributeError: 'str' object has no attribute 'feed'   

Para los curiosos el XML es del 'proyecto OpenStreetMap s mapnik.

+0

pregunta posiblemente relacionados: http://stackoverflow.com/questions/2524299/entity-references-and-lxml – unutbu

+0

no relacionado, porque en ese caso la entidad está definido en realidad. Elimina la definición de la entidad y vuelves a mi pregunta. – Bryce

+0

fyi - alguien puede querer arreglar el/usr/bin/python a/usr/bin/env python ya que la línea shebang es incorrecta para la mayoría de los sistemas. –

Respuesta

11

No estoy seguro de si esto es un error en ElementTree o qué, pero es necesario llamar a UseForeignDTD (True) en el analizador de expansores para comportarse de la manera que lo hacía en el pasado.

Es un poco hacky, pero se puede hacer esto mediante la creación de su propia instancia de ElementTree.Parser, llamando al método en que es instancia de xml.parsers.expat, y luego pasarla a 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 

Este salidas "Mooooo"

o usando una interfaz de mapeo:

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 

Esto da salida a "moo_1"

Una solución más compleja sería subclase ElementTree.XMLParser y solucionarlo allí.

+0

Un poco asqueroso como dices, pero gracias. ¿Hay alguna forma de evitar tener que predefinir las entidades (por ejemplo, y moo_2)? – Bryce

+0

@Bryce: ser predefinido es el objetivo de las entidades, ¿no? Sin embargo, puede establecer 'parser.entity' en su propio objeto similar a un diccionario. Como un ejemplo simple, podrías hacer 'parser.entity = collections.defaultdict (str) 'para que todas las entidades indefinidas sean reemplazadas por una cadena vacía. – Steven

+0

Para seguir el comentario de @ Steven, también podría implementar una interfaz de mapeo y hacer lo que quiera con las teclas. Edité mi respuesta para mostrar un ejemplo simple de eso. – cnelson

3

Como @cnelson ya se ha señalado en un comentario, la solución elegida aquí no va a funcionar en Python 3.

finalmente tengo trabajo. Citado de este Q&A.

Inspirados por this post, podemos anteponer alguna definición XML al contenido HTML sin procesar entrante, y ElementTree funcionaría sin conexión.

Esto funciona tanto para 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) 
Cuestiones relacionadas