2011-01-14 14 views
13

ho un xml come questo:modo efficiente per iterare throught elementi XML

<a> 
    <b>hello</b> 
    <b>world</b> 
</a> 
<x> 
    <y></y> 
</x> 
<a> 
    <b>first</b> 
    <b>second</b> 
    <b>third</b> 
</a> 

ho bisogno di scorrere tutte le <a> e <b> tag, ma non so quanti di loro sono nel documento. Così ho utilizzare xpath per gestire questo:

from lxml import etree 

doc = etree.fromstring(xml) 

atags = doc.xpath('//a') 
for a in atags: 
    btags = a.xpath('b') 
    for b in btags: 
      print b 

Funziona, ma non ho abbastanza file di grandi dimensioni, e cProfile mi dimostra che xpath è molto costoso da usare.

Mi chiedo, forse c'è un modo più efficiente per scorrere il numero indefinitamente di elementi xml?

+1

Si prega di tradurre "abbastanza grande" in megabyte. –

risposta

17

XPath deve essere veloce. È possibile ridurre il numero di XPath chiama ad uno:

doc = etree.fromstring(xml) 
btags = doc.xpath('//a/b') 
for b in btags: 
    print b.text 

Se questo non è abbastanza veloce, si potrebbe provare Liza Daly's fast_iter. Questo ha il vantaggio di non richiedere che l'intero XML venga elaborato prima con etree.fromstring ei nodi parent vengono gettati dopo che i bambini sono stati visitati. Entrambe queste cose aiutano a ridurre i requisiti di memoria. Sotto è a modified version of fast_iter che è più aggressivo sulla rimozione di altri elementi che non sono più necessari.

def fast_iter(context, func, *args, **kwargs): 
    """ 
    fast_iter is useful if you need to free memory while iterating through a 
    very large XML file. 

    http://lxml.de/parsing.html#modifying-the-tree 
    Based on Liza Daly's fast_iter 
    http://www.ibm.com/developerworks/xml/library/x-hiperfparse/ 
    See also http://effbot.org/zone/element-iterparse.htm 
    """ 
    for event, elem in context: 
     func(elem, *args, **kwargs) 
     # It's safe to call clear() here because no descendants will be 
     # accessed 
     elem.clear() 
     # Also eliminate now-empty references from the root node to elem 
     for ancestor in elem.xpath('ancestor-or-self::*'): 
      while ancestor.getprevious() is not None: 
       del ancestor.getparent()[0] 
    del context 

def process_element(elt): 
    print(elt.text) 

context=etree.iterparse(io.BytesIO(xml), events=('end',), tag='b') 
fast_iter(context, process_element) 

Liza Daly's article il parsing di grandi file XML può rivelarsi utile lettura anche a te. Secondo l'articolo, lxml con fast_iter può essere più veloce dello cElementTree. (Vedi Tabella 1).

+0

Qual è lo scopo di 'doc = etree.fromstring (xml)' nel codice fast_iter ?? –

+0

@John Machin: errore di copia-incolla. Grazie per segnalarlo. – unutbu

+0

iterparse speed war: Come dice l'articolo, lxml è più veloce SE si seleziona un particolare tag e per l'analisi generale (è necessario esaminare più tag), cElementTree è più veloce. –

10

Che ne dici di iter?

>>> for tags in root.iter('b'):   # root is the ElementTree object 
...  print tags.tag, tags.text 
... 
b hello 
b world 
b first 
b second 
b third 
+0

Questo link è morto; eccone uno dal vivo: http://lxml.de/tutorial.html#tree-iteration –

5

Uso iterparse:

import lxml.etree as ET 
    for event, elem in ET.iterparse(filelike_object): 
     if elem.tag == "a": 
      process_a(elem) 
      for child in elem: 
       process_child(child) 
      elem.clear() # destroy all child elements 
     elif elem.tag != "b": 
      elem.clear() 

Si noti che questo non salva tutta la memoria, ma sono stato in grado di guadare attraverso flussi XML di oltre un Gb utilizzando questa tecnica.

Prova import xml.etree.cElementTree as ET ... si tratta con Python e il suo iterparse è più veloce rispetto alla lxml.etreeiterparse, secondo the lxml docs:

"" "Per le applicazioni che richiedono un elevato throughput di parser di file di grandi dimensioni, e che fanno poco in assenza di serializzazione, il cET è la scelta migliore, anche per le applicazioni iterparse che estraggono piccole quantità di dati o informazioni aggregate da grandi insiemi di dati XML che non si adattano alla memoria.Se si tratta di prestazioni round-trip, tuttavia, lxml tende ad essere più volte più veloce in totale. Quindi, ogni volta che i documenti di input non sono considerevolmente più grandi dell'output, lxml è il chiaro vincitore. "" "

-2

BS4 è molto utile per questo

from bs4 import BeautifulSoup 
raw_xml = open(source_file, 'r') 
soup = BeautifulSoup(raw_xml) 
soup.find_all('tags') 
Problemi correlati