Извлечение данных из XML с использованием ElementTree в Python - PullRequest
1 голос
/ 25 февраля 2020

У меня есть следующий файл XML, который я должен проанализировать и извлечь из него данные в CSV-файле. В этом файле у меня есть два блока (box_id), которые упакованы в два разных родительских объекта (parent_box_id), а также есть детали содержимого каждого из блоков (элемент sgtin -> info_sgtin).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<doc>
    <info id_reference="2">
        <data_down>
            <tree>
                <box_id>046071598600870568</box_id>
                <parent_box_id>046071598600875594</parent_box_id>
            </tree>
            <tree>
                <box_id>046071598600870575</box_id>
                <parent_box_id>046071598600875595</parent_box_id>
            </tree>
            <tree>
                <sgtin>
                    <info_sgtin>
                        <sgtin>04607008133585B0SE1HVHBGR3A</sgtin>
                        <box_id>046071598600870568</box_id>
                        <gtin>04607008133585</gtin>
                        <series_number>026A</series_number>
                    </info_sgtin>
                </sgtin>
                <parent_box_id>046071598600870568</parent_box_id>
            </tree>
            <tree>
                <sgtin>
                    <info_sgtin>
                        <sgtin>046070081335856F7P78HBVBEH2</sgtin>
                        <box_id>046071598600870568</box_id>
                        <gtin>04607008133585</gtin>
                        <series_number>026A</series_number>
                    </info_sgtin>
                </sgtin>
                <parent_box_id>046071598600870568</parent_box_id>
            </tree>
            <tree>
                <sgtin>
                    <info_sgtin>
                        <sgtin>046070081335854T61H7CSXDE9W</sgtin>
                        <box_id>046071598600870575</box_id>
                        <gtin>04607008133585</gtin>
                        <series_number>026A</series_number>
                    </info_sgtin>
                </sgtin>
                <parent_box_id>046071598600870575</parent_box_id>
            </tree>
        </data_down>
    </info>
</doc>

Для этой цели я решил использовать Elementtree в Python, но проблема в том, что в моем файле XML есть два варианта тега.

Прежде всего я перебираю все детали и захватить значение box_id, но после этого мне нужно go к родительскому элементу и получить parent_box_id, в который упакован этот box_id.

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

parent_box_id       box_id              sgtin                           series_number
046071598600875594  046071598600870568  04607008133585B0SE1HVHBGR3A     026A
046071598600875594  046071598600870568  046070081335856F7P78HBVBEH2     026A
046071598600875595  046071598600870575  046070081335854T61H7CSXDE9W     026A

Но я не могу понять, как получить значение parent_box_id. Буду признателен за любую поддержку со стороны сообщества.

Вот код, который у меня есть:

import csv
import xml.etree.ElementTree as ET

csv.writer(open('result.csv','w'),delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL))

tree = ET.parse('test.xml')
root = tree.getroot()

with open('result.csv','a',newline='') as myfile:
    writer = csv.writer(myfile, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)

    for alist in root.iter('info_sgtin'):
    sgtin = alist.find('sgtin').text
    box_id = alist.find('box_id').text
    series = alist.find('series_number').text

    writer.writerow([sgtin,box_id,series])

Ответы [ 3 ]

0 голосов
/ 25 февраля 2020

Вам нужно l oop для каждого тега <tree> и проверить, есть ли данные, которые вам нужны. Затем соберите его.

import xml.etree.ElementTree


root = xml.etree.ElementTree.parse('data.xml')

# collect parent data
parent_data = {}
for item in root.iter('tree'):
    box_id_match = item.find('box_id')
    parent_box_id_match = item.find('parent_box_id')
    if box_id_match != None:
        parent_data.update({box_id_match.text: parent_box_id_match.text})

data = []
for item in root.iter('tree'):
    sgtin = item.find('sgtin/info_sgtin/sgtin')
    box_id = item.find('sgtin/info_sgtin/box_id')
    series_number = item.find('sgtin/info_sgtin/series_number')
    # collect valid data
    if sgtin != None and box_id != None and series_number != None:
        parent_box_id = parent_data.get(box_id.text)
        data.append([parent_box_id, box_id.text, sgtin.text, series_number.text])

Вывод:

['046071598600875594', '046071598600870568', '04607008133585B0SE1HVHBGR3A', '026A']
['046071598600875594', '046071598600870568', '046070081335856F7P78HBVBEH2', '026A']
['046071598600875595', '046071598600870575', '046070081335854T61H7CSXDE9W', '026A']
0 голосов
/ 25 февраля 2020

Попробуйте это.

from simplified_scrapy import SimplifiedDoc
html = '''
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<doc>
    <info id_reference="2">
        <data_down>
            <tree>
                <box_id>046071598600870568</box_id>
                <parent_box_id>046071598600875594</parent_box_id>
            </tree>
            <tree>
                <box_id>046071598600870575</box_id>
                <parent_box_id>046071598600875594</parent_box_id>
            </tree>
            <tree>
                <sgtin>
                    <info_sgtin>
                        <sgtin>04607008133585B0SE1HVHBGR3A</sgtin>
                        <box_id>046071598600870568</box_id>
                        <gtin>04607008133585</gtin>
                        <series_number>026A</series_number>
                    </info_sgtin>
                </sgtin>
                <parent_box_id>046071598600870568</parent_box_id>
            </tree>
            <tree>
                <sgtin>
                    <info_sgtin>
                        <sgtin>046070081335856F7P78HBVBEH2</sgtin>
                        <box_id>046071598600870568</box_id>
                        <gtin>04607008133585</gtin>
                        <series_number>026A</series_number>
                    </info_sgtin>
                </sgtin>
                <parent_box_id>046071598600870568</parent_box_id>
            </tree>
            <tree>
                <sgtin>
                    <info_sgtin>
                        <sgtin>046070081335854T61H7CSXDE9W</sgtin>
                        <box_id>046071598600870575</box_id>
                        <gtin>04607008133585</gtin>
                        <series_number>026A</series_number>
                    </info_sgtin>
                </sgtin>
                <parent_box_id>046071598600870575</parent_box_id>
            </tree>
        </data_down>
    </info>
</doc>
'''
doc = SimplifiedDoc(html)
boxIds = doc.selects('data_down>tree').notContains('<sgtin>')
dic = {}
for box in boxIds:
    dic[box.box_id.html]=box.parent_box_id.html
datas=[]
boxs = doc.selects('data_down>info_sgtin')
for box in boxs:
    datas.append([dic[box.box_id.html],box.box_id.html,box.sgtin.html,box.series_number.html])

print (datas)

Результат:

[['046071598600875594', '046071598600870568', '04607008133585B0SE1HVHBGR3A', '026A'], ['046071598600875594', '046071598600870568', '046070081335856F7P78HBVBEH2', '026A'], ['046071598600875594', '046071598600870575', '046070081335854T61H7CSXDE9W', '026A']]
0 голосов
/ 25 февраля 2020

Вот решение, использующее XPATH (сначала собирает отображение между box_id и parent_box_id от непосредственных потомков tree). Это то, что вы ищете? Я не уверен, поскольку 046071598600875595 указан в вашем желаемом выводе как parent_box_id для box_id 046071598600870575, и я не знаю, откуда это происходит.

root = etree.parse(fp, parser)
parent_ids = {elem.text: elem.xpath("following-sibling::parent_box_id")[0].text
              for elem in root.xpath("//*/tree/box_id")}

for alist in root.iter('info_sgtin'):
    sgtin = alist.find('sgtin').text
    box_id = alist.find('box_id').text
    series = alist.find('series_number').text
    print(sgtin, parent_ids[box_id], box_id, series)

Вывод:

04607008133585B0SE1HVHBGR3A 046071598600875594 046071598600870568 026A
046070081335856F7P78HBVBEH2 046071598600875594 046071598600870568 026A
046070081335854T61H7CSXDE9W 046071598600875594 046071598600870575 026A

Если ваши файлы были большими и имело смысл итерировать их один раз, то вы можете использовать etree.iterparse с tag=["box_id"] или tag=["tree"]. В первом случае проверьте, наблюдаете ли вы братьев и сестер, которых вы ожидаете в любом случае (sgtin, gtin, series_number или parent_box_id). Если вы найдете parent_box_id, то вы добавите новое сопоставление в свою таблицу поиска (словарь, который связывает box_id s с parent_box_id s. Если вы найдете sgtin и другие, запишите данные, которые вы собрали у братьев и сестер и получите parent_box_id из таблицы поиска.

Конечно, описанное итеративное решение может работать таким образом, только если структура такова, что отображения box_id в parent_box_id всегда предшествуют наборам sgtin, box_id, gtin и series_number.

...