lxml с большим файлом: отфильтруйте поддеревья на основе атрибута - PullRequest
0 голосов
/ 28 октября 2019

Проблема высокого уровня, которую я пытаюсь решить, заключается в том, что у меня есть дамп данных SMS объемом 1,5 ГБ, и я пытаюсь отфильтровать файл, чтобы сохранить только сообщения для одного контакта и из него.

IЯ использую lxml в Python для анализа файла, но дайте мне знать, если есть лучшие варианты.

Структура XML-файла выглядит следующим образом:

SMSES (root node)
  'count': 'xxxx',
  (Children):
      MMS
          'address': 'xxxx',
          'foo':     'bar',
           ... : ...,
           (Children)
               'other fields': 'that _do not_ specify address',
      MMS
          'address': 'xxxx',
          'foo':     'bar',
           ... : ...,
           (Children)
               'other fields': 'that _do not_ specify address'

т.е. я хочуОбходите дочерние узлы корневого узла, и для каждого MMS, в котором «адрес» не соответствует определенному значению, удалите это MMS и все его потомки (дочерние элементы, как правило, содержат такие элементы, как изображения и т. Что я пробовал:

Я нашел вопрос / ответы, подобные этому: как удалить элемент в lxml

Но эти потоки, как правило, имеют простые примеры без вложенныхelements.

  • Мне не понятно, как использовать tree.xpath(), чтобы найти элементы, которые не соответствуют значению
  • Мне не ясно, является ли вызовremove(item) удаляет потомков элемента (которые я хочу вэто случай).

Я попробовал очень наивный подход, при котором я получаю итератор, а затем иду по дереву, удаляя элементы по ходу:

from lxml.etree import XMLParser, parse
p = XMLParser(huge_tree=True)
tree = parse('backup.xml', parser=p)

it = tree.iter()
item = next(it) # consume root node

for item in it:
    if item.attrib['address'] != '0000':
        item.getparent().remove(item)

Проблема с этим сценарием заключается в том, что итератор выполняет DFS, а дочерние элементы элементов не имеют поля адреса. Итак, я ищу:

  • Какой самый эффективный + достаточно простой способ выполнить мою задачу?
  • В противном случае, как я могу заставить tree.iter() дать мне BFSитератор только для соседей первой степени корня?
  • Удаляет ли (элемент) действительно всех потомков или прикрепляет потомков к родителю?

Спасибо, что нашли время для чтения. Извините, если это наивный вопрос - разбор XML-файлов на самом деле не мой хлеб и масло, и документацию по LXML мне было трудно прочитать как новичку.

Спасибо!

1 Ответ

0 голосов
/ 28 октября 2019

На прошлой неделе вышла новая версия Saxon / C с привязкой к языку Python, включающая потоковую возможность XSLT 3.0: это очень новое программное обеспечение, но вы можете попробовать его (с оценочной лицензией Saxon-EE, доступной на saxonica.com). ). Таблица стилей очень проста:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0">

<xsl:mode streamable="yes"/>

<xsl:template match="/">
<SMSES>
   <xsl:copy-of select="SMS[@address='specific value']"/>
</SMSES>
</xsl:template>

</xsl:transform>

К сожалению, вы абстрагировали свой XML, поэтому я не могу сказать, является ли "адрес" на самом деле элементом или атрибутом, и это существенно влияет при потоковой передаче. Здесь я предположил, что это атрибут, но если вы предоставите реальный образец XML, тогда я могу помочь вам создать какой-нибудь реально работающий XSLT-код.

Вы также можете запустить его прямо из командной строки, используя установленныйСаксонский / Java продукт, если нет реальных ограничений, что он должен быть запущен из Python. Но в любом случае для потоковой передачи требуется корпоративная версия Saxon.

...