2011-01-14 4 views
13

i ont un xml comme ceci:moyen efficace pour itérer throught éléments xml

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

je dois parcourir tous <a> et <b> balises, mais je ne sais pas combien d'entre eux sont dans le document. Donc j'utiliser pour gérer xpath que:

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 

Il fonctionne, mais j'ai des fichiers assez gros, et cProfile me montre que xpath est très coûteux à utiliser.

Je me demande, peut-être y at-il une manière plus efficace d'itérer par le nombre indéfini d'éléments xml?

+1

S'il vous plaît traduire « assez grand » en méga-octets. –

Répondre

17

XPath devrait être rapide. Vous pouvez réduire le nombre de XPath appelle à une:

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

Si cela ne suffit pas rapide, vous pouvez essayer Liza Daly's fast_iter. Ceci a l'avantage de ne pas exiger que tout le XML soit traité avec etree.fromstring en premier, et les nœuds parents sont jetés après la visite des enfants. Ces deux éléments aident à réduire les besoins en mémoire. Ci-dessous est a modified version of fast_iter qui est plus agressif sur l'élimination d'autres éléments qui ne sont plus nécessaires.

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 sur l'analyse syntaxique des fichiers XML volumineux peut se révéler utile pour la lecture, vous aussi. Selon l'article, lxml avec fast_iter peut être plus rapide que cElementTreeiterparse. (Voir le tableau 1).

+0

Quel est le but de 'doc = etree.fromstring (xml)' dans le code fast_iter? –

+0

@John Machin: erreur de copier-coller. Merci de l'avoir signalé. – unutbu

+0

iterparse speed war: Comme l'indique l'article, lxml est plus rapide si vous sélectionnez une balise particulière, et pour l'analyse générale (vous devez examiner plusieurs balises), cElementTree est plus rapide. –

10

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

Ce lien est mort; en voici un en direct: http://lxml.de/tutorial.html#tree-iteration –

5

Utilisation 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() 

Notez que cela ne sauve pas toute la mémoire, mais je l'ai été en mesure de patauger dans les flux XML d'utiliser cette technique sur un Gb.

Essayez import xml.etree.cElementTree as ET ... il est livré avec Python et son iterparse est plus rapide que la lxml.etreeiterparse, selon the lxml docs:

« » "Pour les applications nécessitant un débit élevé de l'analyseur de fichiers volumineux, et que pas grand-chose cET est le meilleur choix.Egalement pour les applications iterparse qui extraient de petites quantités de données ou d'informations agrégées à partir de grands ensembles de données XML qui ne rentrent pas dans la mémoire.Si il s'agit de performances aller-retour, lxml a tendance à être Donc, chaque fois que les documents d'entrée ne sont pas considérablement plus grands que la sortie, lxml est le vainqueur. "" "

-2

BS4 est très utile pour cette

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