2011-01-14 16 views
13

mam xml jak poniżej:skutecznym sposobem iteracyjne bezpo¶rednio elementów XML

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

muszę wykonać iterację wszystkich <a> i <b> tagów, ale nie wiem, ilu z nich są w dokumencie. Więc używam xpath obsłużyć że:

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 

To działa, ale mam dość duże pliki, a cProfilexpath pokazuje mi, że jest bardzo drogie w użyciu.

Zastanawiam się, może istnieje skuteczniejszy sposób iteracji poprzez nieokreśloną liczbę elementów xml?

+1

Proszę przetłumaczyć „bardzo duży” w megabajtach. –

Odpowiedz

17

XPath powinien być szybki. Można zmniejszyć ilość XPath zwraca się do jednego:

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

Jeśli nie jest to wystarczająco szybko, można spróbować Liza Daly's fast_iter. Ma to tę zaletę, że nie wymaga, aby cały kod XML był przetwarzany najpierw jako etree.fromstring, a węzły nadrzędne są odrzucane po odwiedzeniu dzieci. Obie te rzeczy pomagają zmniejszyć zapotrzebowanie na pamięć. Poniżej znajduje się a modified version of fast_iter, który jest bardziej agresywny w usuwaniu innych elementów, które nie są już potrzebne.

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 na parsowania dużych plików XML mogą okazać się przydatne do czytania ciebie też. Zgodnie z tym artykułem, lxml z fast_iter może być szybszy niż cElementTree 's iterparse. (Patrz Tabela 1).

+0

Jaki jest cel 'doc = etree.fromstring (xml)' w kodzie fast_iter? –

+0

@John Machin: wklej kopię i wklej. Dziękuję za wskazanie. – unutbu

+0

iterparse speed war: W artykule stwierdza się, że lxml jest szybszy, jeśli wybierzesz jeden konkretny znacznik, a do ogólnego parsowania (musisz zbadać wiele znaczników), cElementTree jest szybszy. –

10

Co powiecie na 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

To połączenie jest martwe; Oto wersja na żywo: http://lxml.de/tutorial.html#tree-iteration –

5

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

Należy pamiętać, że to nie ratuje całą pamięć, ale udało mi się przebrnąć przez strumienie XML nad GB za pomocą tej techniki.

Spróbuj import xml.etree.cElementTree as ET ... chodzi z Pythonem i jego iterparse jest szybszy niż lxml.etreeiterparse, według the lxml docs:

„” "W przypadku zastosowań wymagających wysokiej przepustowości parsera dużych plików, a to niewiele cET to najlepszy wybór, a także aplikacje iterparse, które pobierają niewielkie ilości danych lub agregują informacje z dużych zestawów danych XML, które nie mieszczą się w pamięci. Jeśli jednak chodzi o wydajność w obie strony, lxml wydaje się być wiele razy szybciej, więc gdy dokumenty wejściowe nie są znacznie większe niż dane wyjściowe, lxml jest oczywistym zwycięzcą. "" "

-2

BS4 jest bardzo przydatna do tego

from bs4 import BeautifulSoup 
raw_xml = open(source_file, 'r') 
soup = BeautifulSoup(raw_xml) 
soup.find_all('tags') 
Powiązane problemy