разобрать большой XML в цикле Python - PullRequest
0 голосов
/ 23 сентября 2019

Я хотел бы проанализировать XML (~ 1 ГБ) со следующей структурой:

<Publication creationDateTime="04-AUG-2019 05:22:07">
  <holds>
    <hold>
      <recordType>Standard</recordType>
      <isEnroute>true</isEnroute>
      <holdName>NANLANG</holdName>
      <holdTime>10</holdTime>
      <inbound>
        <courseValue>170</courseValue>
      </inbound>
      <min>
        <altitude>7874</altitude>
      </min>
    </hold>
    <hold>
      <recordType>Standard</recordType>
      <holdName>ZILINA LOM</holdName>
      <holdTime>10</holdTime>
      <inbound>
        <courseValue>243</courseValue>
      </inbound>
      <max>
        <isFlightLevel>true</isFlightLevel>
        <altitude>85</altitude>
      </max>
      <min>
        <altitude>4500</altitude>
      </min>
    </hold>
  </holds>
</Publication>

Я пояснил, что наиболее эффективным способом является использование lxml.etree iterparse method .

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

class Avia:
    def __init__(self, **kwargs):
        for attr in kwargs.keys():
            self.__dict__[attr] = kwargs[attr]

context = ET.iterparse('test.xml')

def xml_fast_iter(context):
    for event, elem in context:
        if elem.tag == 'holdName':
            hold_name = elem.text
        elif elem.tag == 'holdTime':
            hold_time = elem.text
        elif elem.tag == 'courseValue':
            course = float(elem.text)
        elif elem.tag == 'isEnroute':
            hold_enr = elem.text
        # ...

        elem.clear()
        for ancestor in elem.xpath('ancestor-or-self::*'):
            if ancestor.tag == 'min':
                bottom = alt
            if ancestor.tag == 'max':
                top = alt

            while ancestor.getprevious() is not None:
                del ancestor.getparent()[0]

        if elem.tag == 'hold':
            hold_type = 'TER'
            if hold_enr:
                hold_type = 'ENR'
            outbound = course + 180 if course + 180 < 360 else course - 180
            holdPattern = Avia(name=hold_name, time=hold_time, course=course, outbound=outbound, type=hold_type, bottom=bottom, top=top)
            prop_dict = holdPattern.__dict__
            print(prop_dict)
    del context

При попытке печати я явнополучить hold_type = 'ENR' для второго объекта, потому что hold_enr верно для 1-го объекта и не изменяется, пока второй не имеет этого ключа ... При попытке присвоить None всем переменным после for event, elem in context: IВы получите все значения = Нет, кроме последнего, потому что они зацикливаются на каждом элементе.

Как правильно проанализировать все ключи и инициализировать объект?Может быть, мой путь совершенно неверен?

Правильно ли присвоить None переменным после инициализации ?(тогда тип удержания правильный)

1 Ответ

0 голосов
/ 23 сентября 2019

Также прослушайте событие 'start' и инициализируйте ваши переменные в этот момент.

Работать с dict, который вы можете передать Avia() как есть, удобно, поэтому используется генераторфункция (т. е. yield hold).

def xml_fast_iter(xmlfile):
    context = ET.iterparse(xmlfile, events=('start', 'end'))

    for event, elem in context:
        if event == 'start':
            if elem.tag == 'hold':
                hold = {
                    'name': 'define',
                    'time': 'all',
                    'course': 'defaults',
                    'outbound': 'here',
                    'type': 'TER',
                    'bottom': '',
                    'top': '',
                }
                max = {
                    'altitude': 0
                }
                min = {
                    'altitude': 0
                }
            if elem.tag == 'max':
                minmax = max
            if elem.tag == 'min':
                minmax = min
        else:
            if elem.tag == 'holdName':
                hold['name'] = elem.text
            elif elem.tag == 'holdTime':
                hold['time'] = elem.text
            elif elem.tag == 'courseValue':
                course = float(elem.text)
                hold['course'] = course
                hold['outbound'] = course + 180 if course + 180 < 360 else course - 180
            elif elem.tag == 'isEnroute':
                hold['type'] = 'ENR'
            elif elem.tag == 'altitude':
                minmax['altitude'] = int(elem.text)
            elif elem.tag == 'hold':
                hold['bottom'] = min['altitude']
                hold['top'] = max['altitude']
                yield hold

использование:

for hold in xml_fast_iter('test.xml'):
    holdPattern = Avia(**hold)
    prop_dict = holdPattern.__dict__
    print(prop_dict)

Обратите внимание, что то, что я сделал с 'max' и 'min', - всего лишь предположение о том, что вам нужно, но он показывает, как вы можете работать с контекстными данными, не прибегая к XPath, как ./ancestor-or-self::*, что было бы намного медленнее по сравнению.

...