2011-01-14 2 views
13

У меня есть XML, как это:Эффективный способ для перебора элементов XML, используя имеющийся

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

Мне нужно перебрать все <a> и <b> тегов, но я не знаю, как многие из них находятся в документе. Поэтому я использую xpath справиться с этим:

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 

Он работает, но у меня есть очень большие файлы, и cProfile показывает мне, что xpath очень дорого использовать.

Интересно, может быть, есть более эффективный способ перебора бесконечного числа xml-элементов?

+1

Пожалуйста перевод «довольно большой» в мегабайтах. –

ответ

17

XPath должен быть быстрым. Вы можете уменьшить количество вызовов XPath к одному:

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

Если это не достаточно быстро, вы можете попробовать Liza Daly's fast_iter. Это имеет то преимущество, что не требуется, чтобы весь XML обрабатывался сначала с etree.fromstring, а родительские узлы выбрасывались после того, как дети были посещены. Обе эти вещи помогают снизить требования к памяти. Ниже приведено a modified version of fast_iter, которое более агрессивно относится к удалению других элементов, которые больше не нужны.

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 на анализ больших XML-файлов может оказаться полезным чтение для вас. Согласно статье, lxml с fast_iter может быть быстрее cElementTree's iterparse. (См. Таблицу 1).

+0

Какова цель 'doc = etree.fromstring (xml)' в коде fast_iter? –

+0

@John Machin: ошибка копирования-вставки. Спасибо, что указали это. – unutbu

+0

iterparse speed war: Как говорится в статье, lxml быстрее, если вы выберете один конкретный тег, а для общего анализа (вам нужно изучить несколько тегов), cElementTree работает быстрее. –

10

Как насчет 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

Эта ссылка мертва; вот живой: http://lxml.de/tutorial.html#tree-iteration –

5

Использование 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() 

Обратите внимание, что это не спасает всю память, но я смог пробраться через XML-потоков над Gb, используя эту технику.

Попробуйте import xml.etree.cElementTree as ET ... он поставляется с Python и его iterparse быстрее, чем lxml.etreeiterparse, согласно the lxml docs:

«» "Для приложений, требующих высокой пропускной способности синтаксического анализа больших файлов, и что мало без сериализации, cET - лучший выбор. Также для iterparse-приложений, которые извлекают небольшие объемы данных или агрегированную информацию из больших наборов данных XML, которые не вписываются в память. Однако, если дело доходит до производительности в обоих направлениях, lxml имеет тенденцию быть в несколько раз быстрее. Таким образом, всякий раз, когда входные документы не намного превышают выходные данные, lxml является явным победителем. "" "

-2

BS4 очень полезно для этого

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