элементы iterparse очищаются, прежде чем я смогу захватить данные

Я пытаюсь использовать Python для анализа большого файла XML (27 ГБ), используя cElementTree и iterparse. Я могу извлечь все теги, но по какой-то причине ни один текст элемента не извлекается (он всегда показывает «Нет»). Я проверил документацию и StackOverflow, но безрезультатно. В крайнем случае я попробовал анализ с помощью lxml, и он работает, но я бы предпочел, если это возможно, выяснить это на cElementree. Обновление: когда я комментирую строку elem.clear(), она показывает анализируемые данные, но теперь я пытаюсь понять, почему метод clear() стирает данные до того, как они будут напечатаны (в конечном итоге я хочу поместить данные в отдельную структуру данных, например базу данных). Я предполагаю, что мне нужно очистить данные, чтобы я не использовал память во время разбора файла. Это одна из тех ситуаций, когда «все в Python является объектом»?

Используя меньший образец, извлеченный из файла, я все еще получаю ту же ошибку. XML-файл выглядит примерно так (хотя в нем гораздо больше записей):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><entityList><entity 
xmlns:ns2="urn:hl7-org:v3" xmlns:ns3="urn:axolotl-com:pdo">
<fragmentId>d68e616e-a6bc-4630-b104-3891859a8ce4</fragmentId>
<aggregateId>H1060734453</aggregateId>
<source>b6167864-5f74-40e5-97c5-7e551a3a4a7d</source>
<sourceName>SHM ADT</sourceName>
<sourceOid>2.16.840.1.113883.3.2.2.3.1.21.3</sourceOid>
<sourceAaoid>2.16.840.1.113883.3.62.2</sourceAaoid>
</entity></entityList>

Вот фрагмент некорректно работающего кода:

import xml.etree.ElementTree as etree
xml=r'C:\sample.xml'

count = 0

for event, elem in etree.iterparse(xml):
    if event == 'end':
        if elem.tag == 'entity':
            count+=1        
                for child in elem:
                    print (child.tag, child.attrib, child.text)
    elem.clear()
print(count)

я собираюсь

fragmentId {} None
aggregateId {} None
source {} None
sourceName {} None
sourceOid {} None
sourceAaoid {} None

Почему elem.clear() стирает текст, хотя кажется, что сначала должна произойти печать? Какие-либо предложения?


person Derek Plansky    schedule 04.01.2019    source источник


Ответы (2)


Перемещение elem.clear() в блок под оператором if elem.tag == 'entity': работает. Это гарантирует, что дочерние элементы будут очищены только после того, как вы их обработаете.

count = 0

for event, elem in etree.iterparse(xml):
    if event == 'end':
        if elem.tag == 'entity':
            count+=1        
            for child in elem:
                print (child.tag, child.attrib, child.text)
            elem.clear()    # Clear only if </entity> is encountered
print(count)

В исходном примере к моменту появления закрывающего тега </entity> все дочерние элементы уже очищены (их закрывающие теги встречаются ранее).

count = 0

for event, elem in etree.iterparse(xml):
    if event == 'end':
        if elem.tag == 'entity':
            count+=1        
                for child in elem:
                    print (child.tag, child.attrib, child.text)
    elem.clear()    # Clears fragmentId ... sourceAaoid before </entity>
print(count)
person pizza    schedule 25.11.2020

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

import xml.etree.ElementTree as ET

tree = ET.parse(path_to_xml)
root = tree.getroot()

def tree_parser(root):
    for child in root.getchildren():
        if not child.getchildren():
            print(child.tag, child.text)
        else:
            tree_parser(child)

tree_parser(root) 

fragmentId d68e616e-a6bc-4630-b104-3891859a8ce4
aggregateId H1060734453
source b6167864-5f74-40e5-97c5-7e551a3a4a7d
sourceName SHM ADT
sourceOid 2.16.840.1.113883.3.2.2.3.1.21.3
sourceAaoid 2.16.840.1.113883.3.62.2

Согласно вашему комментарию:

def tree_parser(root, seen=set()):
    for child in root.getchildren():
        if not child.getchildren():
            data = (child.tag, child.text)
            seen.add(data)
        else:
            tree_parser(child, seen)
    return seen

for _, element in etree.iterparse(path_to_xml):
    c = tree_parser(element)

print(c)

{('aggregateId', 'H1060734453'),
 ('fragmentId', 'd68e616e-a6bc-4630-b104-3891859a8ce4'),
 ('source', 'b6167864-5f74-40e5-97c5-7e551a3a4a7d'),
 ('sourceAaoid', '2.16.840.1.113883.3.62.2'),
 ('sourceName', 'SHM ADT'),
 ('sourceOid', '2.16.840.1.113883.3.2.2.3.1.21.3')}
person gold_cy    schedule 04.01.2019
comment
можете ли вы заставить это работать с помощью iterparse и elem.clear()? У меня нет памяти, чтобы сначала взять весь файл в память. Спасибо. - person Derek Plansky; 04.01.2019