2009-10-03 19 views

Respuesta

6

"Al vuelo" los árboles de documentos y documentos no son realmente compatibles. Los analizadores de estilo SAX generalmente se usan para eso (por ejemplo, el estándar de Python xml.sax). Básicamente, debe definir una clase con controladores para varios eventos como startElement, endElement, etc. y el analizador llamará a los métodos mientras analiza el archivo XML.

+1

eso es lo que quiero ... No me importa tener que "reaccionar" a eventos como "etiqueta de inicio", etc. – jldupont

+1

@ Jean-Lou: si no necesitas todo el árbol, entonces SAX es el camino a seguir. Está hecho para procesar documentos como una secuencia de eventos en lugar de un árbol de contenido. –

4

PullDom hace lo que quiere. Lee XML de una secuencia, como SAX, pero luego crea un DOM para una parte seleccionada de ella.

"PullDOM es una API realmente simple para trabajar con objetos DOM de forma continua (¡eficiente!) Y no como un árbol monolítico."

+0

así que si pongo una declaración "yield" en for-loop {p. Ej. for (event, node) en events: yield (event, node)} PullDom no se reiniciará al comienzo la próxima vez que ingrese for-loop? – jldupont

+0

... porque eso es lo que sucede con "iterparse" ... – jldupont

+0

@ Jean-Lou Dupont: si quiere un comportamiento iterativo, ¿debería llamar a 'iter (...)' en el objeto ElementTree? – u0b34a0f6ae

15

xml.etree.cElementTree viene cerca de un generador con el uso correcto; de forma predeterminada, recibe cada elemento después de su evento 'final', en cuyo punto puede procesarlo. Debe usar element.clear() en el elemento si no lo necesita después del procesamiento; así ahorras la memoria.


Aquí hay un ejemplo completo de lo que quiero decir, donde analizo la biblioteca de Rhythmbox (reproductor de música). Utilizo iterparse de (c) ElementTree y para cada elemento procesado llamo element.clear() para que guarde una gran cantidad de memoria. (Por cierto, el siguiente código es un sucesor de algún código sax para hacer lo mismo, la solución cElementTree fue un alivio ya que 1) El código es conciso y expresa lo que necesito y nada más 2) Es 3 veces más rápido, 3) que utiliza menos memoria.)

import os 
import xml.etree.cElementTree as ElementTree 
NEEDED_KEYS= set(("title", "artist", "album", "track-number", "location",)) 

def _lookup_string(string, strmap): 
    """Look up @string in the string map, 
    and return the copy in the map. 

    If not found, update the map with the string. 
    """ 
    string = string or "" 
    try: 
     return strmap[string] 
    except KeyError: 
     strmap[string] = string 
     return string 

def get_rhythmbox_songs(dbfile, typ="song", keys=NEEDED_KEYS): 
    """Return a list of info dictionaries for all songs 
    in a Rhythmbox library database file, with dictionary 
    keys as given in @keys. 
    """ 
    rhythmbox_dbfile = os.path.expanduser(dbfile) 

    lSongs = [] 
    strmap = {} 

    # Parse with iterparse; we get the elements when 
    # they are finished, and can remove them directly after use. 

    for event, entry in ElementTree.iterparse(rhythmbox_dbfile): 
     if not (entry.tag == ("entry") and entry.get("type") == typ): 
      continue 
     info = {} 
     for child in entry.getchildren(): 
      if child.tag in keys: 
       tag = _lookup_string(child.tag, strmap) 
       text = _lookup_string(child.text, strmap) 
       info[tag] = text 
     lSongs.append(info) 
     entry.clear() 
    return lSongs 

Ahora, no entiendo sus expectativas, ¿tiene la siguiente expectativa?

# take one 
for event, entry in ElementTree.iterparse(rhythmbox_dbfile): 
    # parse some entries, then exit loop 

# take two 
for event, entry in ElementTree.iterparse(rhythmbox_dbfile): 
    # parse the rest of entries 

¡Cada vez que llame a iterparse obtendrá un nuevo objeto iterador, leyendo el archivo de nuevo! Si desea que un objeto persistente con la semántica de iterador, hay que hacer referencia al mismo objeto en ambos bucles (código no probado):

#setup 
parseiter = iter(ElementTree.iterparse(rhythmbox_dbfile)) 
# take one 
for event, entry in parseiter: 
    # parse some entries, then exit loop 

# take two 
for event, entry in parseiter: 
    # parse the rest of entries 

creo que puede ser confuso ya que los diferentes objetos tienen una semántica diferente. Un objeto de archivo siempre tendrá un estado interno y avance en el archivo, independientemente de cómo lo itere. Un objeto Iterparse ElementTree aparentemente no. Lo crucial es pensar que cuando usas un ciclo for, el for siempre llama a iter() en el objeto sobre el que iteras. Aquí es un experimento comparando ElementTree.iterparse con un objeto de archivo:

>>> import xml.etree.cElementTree as ElementTree 
>>> pth = "/home/ulrik/.local/share/rhythmbox/rhythmdb.xml" 
>>> iterparse = ElementTree.iterparse(pth) 
>>> iterparse 
<iterparse object at 0x483a0890> 
>>> iter(iterparse) 
<generator object at 0x483a2f08> 
>>> iter(iterparse) 
<generator object at 0x483a6468> 
>>> f = open(pth, "r") 
>>> f 
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98> 
>>> iter(f) 
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98> 
>>> iter(f) 
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98> 

Lo que se ve es que cada llamada a iter() sobre un objeto iterparse devuelve un nuevo generador. Sin embargo, el objeto de archivo tiene un estado del sistema operativo interno que debe conservarse y es su propio iterador.

+0

@kaizer: Entonces, en realidad, es como trabajar con el subconjunto del documento cada vez que se ingresa for-loop después del elemento.clear()? – jldupont

+0

No ha definido lo que quiere hacer y sus expectativas me sorprenden; Yo usaría iterparse en uno para recorrer todo el documento. Haré un ejemplo. – u0b34a0f6ae

+0

@kaizer: muchas gracias por todos sus esfuerzos. Descubrí el analizador de SAX gracias a esta publicación y parece que podré gestionar la compilación de mi analizador basado en máquina de estado de forma precisa con este enfoque. (¿Puedes decir que soy un novato en XML? ;-) – jldupont

0

Esto es posible con elementtree y el análisis elemental: http://effbot.org/zone/element-iterparse.htm#incremental-parsing

import xml.etree.cElementTree as etree 
for event, elem in etree.iterparse(source): 
    ... 

fácil de usar que el saxofón.

+0

@jldupont: Su pregunta dice que usted intentó eso (hace dos años): "" "He intentado con" iterparse "de" xml.etree.cElementTree "(que es realmente agradable)" "" –

+0

-1 Archivo grande significa use cElementTree (¡que los estados OP ya han sido probados!) ... ¿no leyó la respuesta de @ kaiser.se? –

Cuestiones relacionadas