2009-10-03 14 views

risposta

6

"Al volo" l'analisi e la documentazione degli alberi non sono realmente compatibili. I parser in stile SAX vengono solitamente utilizzati per questo (ad esempio, lo standard di Python xml.sax). Fondamentalmente devi definire una classe con gestori per vari eventi come startElement, endElement, ecc. E il parser chiamerà i metodi mentre analizza il file XML.

+1

è quello che voglio ... Non mi interessa dover "reagire" ad eventi come "tag iniziale" ecc. – jldupont

+1

@ Jean-Lou: se non è necessario l'intero albero, allora SAX è il ben fatto. È fatto per elaborare i documenti come un flusso di eventi anziché come un albero di contenuto. –

4

PullDom fa quello che vuoi. Legge XML da uno stream, come SAX, ma poi costruisce un DOM per un pezzo selezionato.

"PullDOM è un'API davvero semplice per lavorare con oggetti DOM in modo efficiente (efficiente!) Piuttosto che come albero monolitico."

+0

quindi se inserisco un'istruzione "yield" nel ciclo for {ad es. per (evento, nodo) negli eventi: yield (evento, nodo)} PullDom non si riavvierà all'inizio la prossima volta che inserirò il ciclo? – jldupont

+0

... perché è quello che succede con "iterparse" ... – jldupont

+0

@ Jean-Lou Dupont: se vuoi il comportamento dell'iteratore, forse dovresti chiamare 'iter (...)' sull'oggetto ElementTree? – u0b34a0f6ae

15

xml.etree.cElementTree si avvicina a un generatore con utilizzo corretto; per impostazione predefinita si riceve ogni elemento dopo il suo evento 'fine', a quel punto è possibile elaborarlo. Dovresti usare element.clear() sull'elemento se non ne hai bisogno dopo l'elaborazione; in tal modo si salva la memoria.


Ecco un esempio completo di cosa intendo, dove analizzo la libreria di Rhythmbox (Music Player). Uso (it) l'elemento iterparse di ElementTree e per ogni elemento elaborato chiamo element.clear() in modo da risparmiare parecchia memoria. (A proposito, il codice seguente è un successore di qualche codice sax per fare la stessa cosa, la soluzione cElementTree era un sollievo dal 1) Il codice è conciso ed esprime quello che mi serve e niente di più 2) È 3 volte più veloce, 3) utilizza meno 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 

Ora, non capisco le vostre aspettative, avete le seguenti aspettative?

# 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 

Ogni volta che si chiama iterparse si ottiene un nuovo oggetto iteratore, la lettura del file di nuovo! Se si desidera un oggetto persistente con la semantica iteratore, si deve fare riferimento allo stesso oggetto in entrambi i loop (codice non provato):

#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 

penso che possa essere fonte di confusione in quanto oggetti diversi hanno diversi semantica. Un oggetto file avrà sempre uno stato interno e avanzerà nel file, indipendentemente dall'iterazione su di esso. Apparentemente un oggetto iterparse ElementTree non lo è. Il punto cruciale è pensare che quando si utilizza un ciclo for, il per sempre chiama iter() sulla cosa su cui si sta iterando. Ecco un esperimento confrontando ElementTree.iterparse con un oggetto file:

>>> 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> 

Quello che si vede è che ogni chiamata a ITER() su un oggetto iterparse restituisce un nuovo generatore. L'oggetto file tuttavia ha uno stato del sistema operativo interno che deve essere conservato e il suo iteratore.

+0

@kaizer: Quindi in effetti è come lavorare con il sottoinsieme del documento ogni volta che il ciclo viene inserito dopo il element.clear()? – jldupont

+0

Non hai definito cosa vuoi fare e le tue aspettative mi sorprendono; Vorrei usare iterparse in un ciclo for su tutto il documento. Farò un esempio – u0b34a0f6ae

+0

@kaizer: molte grazie per tutti i vostri sforzi. Ho scoperto il parser SAX grazie a questo post e sembra che sarò in grado di gestire ordinatamente il mio parser basato sullo stato macchina con questo approccio. (Puoi dire che sono un principiante di XML? ;-) – jldupont

0

Questo è possibile con elementtree e analisi incrementale: http://effbot.org/zone/element-iterparse.htm#incremental-parsing

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

facile da usare rispetto sax.

+0

@jldupont: La tua domanda dice che l'hai provato (due anni fa): "" "Ho provato" iterparse "di" xml.etree.cElementTree "(che è davvero bello)" "" –

+0

-1 File di grandi dimensioni significa usa cElementTree (che gli stati OP sono già stati provati!) ... non hai letto la risposta di @ kaiser.se? –