2009-10-03 3 views
8

Я бы хотел разобрать большой XML-файл «на лету». Я бы хотел использовать генератор питона для выполнения этого. Я пробовал «iterparse» из «xml.etree.cElementTree» (что очень приятно), но все же не является генератором.python: есть ли синтаксический анализатор XML, реализованный в качестве генератора?

Другие предложения?

ответ

6

«На лету» синтаксический анализ и деревья документов на самом деле несовместимы. Для этого обычно используются парсеры SAX-стиля (например, стандарт Python xml.sax). В основном вы должны определить класс с обработчиками для различных событий, таких как startElement, endElement и т. Д., И парсер будет вызывать методы при анализе XML-файла.

+1

вот что я хочу ... Я не против «реагировать» на такие события, как «начальный тег» и т. Д. – jldupont

+1

@ Жан-Лу: если вам не нужно все дерево, то SAX - это путь. Он предназначен для обработки документов как поток событий вместо дерева контента. –

4

PullDom делает, что вы хотите. Он читает XML из потока, например SAX, но затем строит DOM для выбранного фрагмента.

«PullDOM - очень простой API для работы с объектами DOM в потоковом (эффективном!) Способе, а не как монолитное дерево».

+0

, так что если я поставлю оператор «yield» в for-loop {например. for (event, node) в событиях: yield (event, node)} PullDom не будет перезагружаться в начале следующего раза, когда я вхожу в for-loop? – jldupont

+0

... потому что это то, что происходит с «iterparse» ... – jldupont

+0

@ Jean-Lou Dupont: если вы хотите поведения итератора, возможно, вам следует называть 'iter (...)' на объекте ElementTree? – u0b34a0f6ae

15

xml.etree.cElementTree подходит к генератору с правильным использованием; по умолчанию вы получаете каждый элемент после события «end», после чего вы можете его обработать. Вы должны использовать element.clear() для элемента, если он не понадобится после обработки; тем самым вы сохраняете память.


Вот полный пример того, что я имею в виду, когда я разбираю библиотеку Rhythmbox (Music Player). Я использую (c) iterparse ElementTree и для каждого обработанного элемента я вызываю element.clear(), так что я сохраняю довольно много памяти. (Btw, нижеприведенный код является преемником некоторого кода саксофона, чтобы сделать то же самое, решение cElementTree было облегчением, так как 1) Код сжатый и выражает то, что мне нужно, и не более 2) Он 3 раза быстрее, 3) она использует меньше памяти.)

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 

Теперь, я не понимаю ваших ожиданий, у вас есть следующие ожидания?

# 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 

Каждый раз, когда вы вызываете iterparse, вы получаете новый объект итератора, считывая файл заново! Если вы хотите постоянный объект с итераторными семантиками, вы должны ссылаться на тот же объект в обеих петлях (непроверенный код):

#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 

Я думаю, что это может привести к путанице, так как различные объекты имеют различную семантику. Объект файла всегда будет иметь внутреннее состояние и продвигаться в файле, однако вы повторяете его. Объект ElementTree iterparse, по-видимому, нет. Суть заключается в том, чтобы думать, что, когда вы используете цикл for, for всегда вызывает iter() на том, что вы перебираете. Вот эксперимент сравнения ElementTree.iterparse с объектом файла:

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

То, что вы видите, что каждый вызов ИТЭР() на iterparse объекта возвращает новый генератор. Однако объект-файл имеет внутреннее состояние операционной системы, которое должно быть сохранено, и это его собственный итератор.

+0

@kaizer: Так как это работает с подмножеством документа каждый раз, когда for-loop вводится после элемента element.clear()? – jldupont

+0

Вы не определили, что хотите, и ваши ожидания удивят меня; Я бы использовал iterparse в одном для цикла по всему документу. Я приведу пример. – u0b34a0f6ae

+0

@kaizer: Большое спасибо за все ваши усилия. Я обнаружил анализатор SAX благодаря этому сообщению, и похоже, что я смогу эффективно управлять своим парсером, основанным на государственной машине, с этим подходом. (Можете ли вы сказать, что я новичок XML? ;-) – jldupont

0

Это возможно с ElementTree и дополнительным разбором: http://effbot.org/zone/element-iterparse.htm#incremental-parsing

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

легче использовать, чем саксофон.

+0

@jldupont: В вашем вопросе говорится, что вы пробовали это (два года назад): "" "Я пробовал" iterparse "из" xml.etree.cElementTree "(что очень приятно)" "" –

+0

-1 Большие файлы означает используйте cElementTree (который уже был проверен OP-состояниями!) ... разве вы не читали ответ by @ kaiser.se? –

Смежные вопросы