Python sax для lxml для 80 + GB XML

Как бы вы прочитали XML-файл с использованием sax и преобразовали его в элемент lxml etree.iterparse?

Чтобы предоставить обзор проблемы, я создал инструмент для анализа XML с использованием lxml для XML-фида, который будет варьироваться в размере 25-500 МБ, который требует приема внутрь на двухдневной основе, но ему необходимо выполнить однократное проглатывание файл размером 60 – 100 ГБ.

Я решил использовать lxml на основе спецификаций, которые детализировали узел, не превышал бы размер 4-8 ГБ, который, как я думал, позволит считывать информацию в памяти и очищаться по завершении.

Обзор, если код ниже

elements = etree.iterparse( self._source, events = ('end',) ) for event, element in elements: finished = True if element.tag == 'Artist-Types': self.artist_types(element) def artist_types(self, element): """ Imports artist types :param list element: etree.Element :returns boolean: """ self._log.info("Importing Artist types") count = 0 for child in element: failed = False fields = self._getElementFields(child, ( ('id', 'Id'), ('type_code', 'Type-Code'), ('created_date', 'Created-Date') )) if self._type is IMPORT_INC and has_artist_type(fields['id']): if update_artist_type(fields['id'], fields['type_code']): count = count + 1 else: failed = True else: if create_artist_type(fields['type_code'], fields['created_date'], fields['id']): count = count + 1 else: failed = True if failed: self._log.error("Failed to import artist type %s %s" % (fields['id'], fields['type_code']) ) self._log.info("Imported %d Artist Types Records" % count) self._artist_type_count = count self._cleanup(element) del element 

Дайте мне знать, могу ли я добавить какие-либо разъяснения.

2 Solutions collect form web for “Python sax для lxml для 80 + GB XML”

iterparse – итеративный синтаксический анализатор. Он будет испускать объекты и события Element и постепенно наращивать все дерево Element при его анализе, поэтому в итоге он будет иметь все дерево в памяти.

Тем не менее, легко иметь ограниченное поведение в памяти: удалите ненужные элементы, когда вы их разбираете.

Типичная рабочая нагрузка «гигантский xml» – это один корневой элемент с большим количеством дочерних элементов, которые представляют записи. Я предполагаю, что это такая структура XML, с которой вы работаете?

Обычно достаточно использовать clear() для удаления элемента, который вы обрабатываете. Использование вашей памяти будет немного расти, но это не очень. Если у вас действительно огромный файл, то даже пустые объекты Element будут потреблять слишком много, и в этом случае вы также должны удалить ранее просматриваемые объекты Element . Обратите внимание, что вы не можете безопасно удалить текущий элемент. Документация lxml.etree.iterparse описывает этот метод .

В этом случае вы будете обрабатывать запись каждый раз, когда будет найдена </record> , тогда вы удалите все предыдущие элементы записи.

Ниже приведен пример использования бесконечно длинного XML-документа. Он будет печатать использование памяти процесса, когда он анализирует. Обратите внимание, что использование памяти стабильно и не продолжает расти.

 from lxml import etree import resource class InfiniteXML (object): def __init__(self): self._root = True def read(self, len=None): if self._root: self._root=False return "<?xml version='1.0' encoding='US-ASCII'?><records>\n" else: return """<record>\n\t<ancestor attribute="value">text value</ancestor>\n</record>\n""" def parse(fp): context = etree.iterparse(fp, events=('end',)) for action, elem in context: if elem.tag=='record': # processing goes here pass #memory usage print resource.getrusage(resource.RUSAGE_SELF).ru_maxrss # cleanup # first empty children from current element # This is not absolutely necessary if you are also deleting siblings, # but it will allow you to free memory earlier. elem.clear() # second, delete previous siblings (records) while elem.getprevious() is not None: del elem.getparent()[0] # make sure you have no references to Element objects outside the loop parse(InfiniteXML()) 

Я нашел этот полезный пример на http://effbot.org/zone/element-iterparse.htm . Смелый акцент – мой.

Инкрементальный анализ #

Обратите внимание, что iterparse все еще строит дерево, как и синтаксический анализ, но вы можете безопасно изменить или удалить части дерева при разборе. Например, чтобы анализировать большие файлы, вы можете избавиться от элементов, как только вы их обработали:

 for event, elem in iterparse(source): if elem.tag == "record": ... process record elements ... elem.clear() 

Вышеупомянутый шаблон имеет один недостаток; он не очищает корневой элемент, поэтому вы получите один элемент с множеством пустых дочерних элементов. Если ваши файлы огромны, а не просто большие, это может быть проблемой. Чтобы обойти это, вам нужно взять верх над корневым элементом. Самый простой способ сделать это – включить начальные события и сохранить ссылку на первый элемент переменной:

 # get an iterable context = iterparse(source, events=("start", "end")) # turn it into an iterator context = iter(context) # get the root element event, root = context.next() for event, elem in context: if event == "end" and elem.tag == "record": ... process record elements ... root.clear() 

(в будущих версиях будет проще получить доступ к корневому элементу из цикла)

  • Lazy SAX XML-анализатор с остановкой / возобновлением
  •  
    Interesting Posts for Van-Lav
    Python - лучший язык программирования в мире.