Подтвердить что ты не робот

Стратегия ElementTree iterparse

Мне приходится обрабатывать XML-документы, которые достаточно большие (до 1 ГБ) и анализировать их с помощью python. Я использую iterparse() (разбор стиля SAX).

Меня беспокоит следующее: представьте, что у вас есть xml, как этот

<?xml version="1.0" encoding="UTF-8" ?>
<families>
  <family>
    <name>Simpson</name>
    <members>
        <name>Hommer</name>
        <name>Marge</name>
        <name>Bart</name>
    </members>
  </family>
  <family>
    <name>Griffin</name>
    <members>
        <name>Peter</name>
        <name>Brian</name>
        <name>Meg</name>
    </members>
  </family>
</families>

Проблема заключается, конечно, в том, чтобы знать, когда я получаю фамилию (как Симпсоны), и когда я получаю имя одного из членов этого семейства (например, Hommer)

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

import xml.etree.cElementTree as ET

__author__ = 'moriano'

file_path = "test.xml"
context = ET.iterparse(file_path, events=("start", "end"))

# turn it into an iterator
context = iter(context)
on_members_tag = False
for event, elem in context:
    tag = elem.tag
    value = elem.text
    if value :
        value = value.encode('utf-8').strip()

    if event == 'start' :
        if tag == "members" :
            on_members_tag = True

        elif tag == 'name' :
            if on_members_tag :
                print "The member of the family is %s" % value
            else :
                print "The family is %s " % value

    if event == 'end' and tag =='members' :
        on_members_tag = False
    elem.clear()

И это отлично работает, поскольку вывод

The family is Simpson 
The member of the family is Hommer
The member of the family is Marge
The member of the family is Bart
The family is Griffin 
The member of the family is Peter
The member of the family is Brian
The member of the family is Meg

Меня беспокоит то, что с помощью этого (простого) примера мне пришлось создать дополнительную переменную, чтобы знать, в каком теге я (on_members_tag) представить с помощью истинных примеров xml, которые мне нужно обрабатывать, у них больше вложенных тегов.

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

Итак, вопрос есть. Я делаю здесь что-то ужасно глупо? Я чувствую, что должно быть более элегантное решение.

4b9b3361

Ответ 1

Здесь один из возможных подходов: мы сохраняем список путей и заглядываем назад, чтобы найти родительский node (s).

path = []
for event, elem in ET.iterparse(file_path, events=("start", "end")):
    if event == 'start':
        path.append(elem.tag)
    elif event == 'end':
        # process the tag
        if elem.tag == 'name':
            if 'members' in path:
                print 'member'
            else:
                print 'nonmember'
        path.pop()

Ответ 2

pulldom отлично подходит для этого. Вы получаете поток sax. Вы можете перебирать поток, и когда вы найдете node, который вас интересует, загрузите этот node в фрагмент dom.

import xml.dom.pulldom as pulldom
import xpath # from http://code.google.com/p/py-dom-xpath/

events = pulldom.parse('families.xml')
for event, node in events:
    if event == 'START_ELEMENT' and node.tagName=='family':
        events.expandNode(node) # node now contains a dom fragment
        family_name = xpath.findvalue('name', node)
        members = xpath.findvalues('members/name', node)
        print('family name: {0}, members: {1}'.format(family_name, members))

выход:

family name: Simpson, members: [u'Hommer', u'Marge', u'Bart']
family name: Griffin, members: [u'Peter', u'Brian', u'Meg']