Преобразование файла Dynami c XML в файл CSV - Python - PullRequest
0 голосов
/ 17 июня 2020

Я хотел бы преобразовать этот XML файл:

<record id="idOne">
    <ts date="2019-07-03" time="15:28:41.720440">5</ts>
    <ts date="2019-07-03" time="15:28:42.629959">10</ts>
    <ts date="2019-07-03" time="15:28:43.552677">15</ts>
    <ts date="2019-07-03" time="15:28:43.855345">20</ts>
</record>

<record id="idOne">
    <ts date="2019-07-03" time="15:28:45.072922">30</ts>
    <ts date="2019-07-03" time="15:28:45.377087">35</ts>
    <ts date="2019-07-03" time="15:28:46.316321">40</ts>
    <ts date="2019-07-03" time="15:28:47.527960">45</ts>
</record>

в этот CSV-файл:

ID, date, time, value
idOne, 2019-07-03, 15:28:41.720440, 5
idOne, 2019-07-03, 15:28:42.629959, 10
idOne, 2019-07-03, 15:28:43.552677, 15
idOne, 2019-07-03, 15:28:43.855345, 20
idOne, 2019-07-03, 15:28:45.072922, 30
idOne, 2019-07-03, 15:28:45.377087, 35
idOne, 2019-07-03, 15:28:46.316321, 40
idOne, 2019-07-03, 15:28:47.527960, 45

У меня может быть несколько тел структур идентификаторов.

Я использую библиотеку l xml.

Я пробовал использовать метод xpath и l oop, но я могу получить только идентификатор, но не остальные. Проблема вторая для l oop, но я не знаю, как обращаться со значениями «дата» и «время» ...

with open(args.input, "r") as f:
    # add root balises to parse the xml file
    records = itertools.chain('<root>', f, '</root>')
    root = etree.fromstringlist(records)

    #root = etree.fromstring(records)
    # count the number of records
    NumberRecords = int(root.xpath('count(//record)'))

    RecordsGrid = [[] for __ in range(NumberRecords)]
    tss = ["id","date", "time", "value"]
    paths = root.xpath('//record')
    #print(paths)
    Counter = 0
    for path in paths:

        for ts in tss[:1]:
            target = f'(./@{ts})'  # using f-strings to populate the full path
            if path.xpath(target):
                # we start populating our current sublist with the relevant info
                RecordsGrid[Counter].append(path.xpath(target)[0])
            else:
                RecordsGrid[Counter].append('NA')

        for ts in tss[1:]:  
            target = f'(./ts[@name="{ts}"]/text())'
            if path.xpath(target):
                RecordsGrid[Counter].append(path.xpath(target)[0])
            else:
                RecordsGrid[Counter].append('NA')
        Counter += 1

    # now that we have our lists, create a df
    df = pd.DataFrame(RecordsGrid, columns=tss)
    df.to_csv(args.output, sep=',', encoding='utf-8', index=False)

Вот результат:

id,date,time,value
idOne,NA,NA,NA

Спасибо за ваше время.

Ответы [ 2 ]

1 голос
/ 17 июня 2020

для решения вашей проблемы я написал следующий скрипт

from bs4 import BeautifulSoup as bs

data = list()

with open("data.xml") as xml:
    data_xml = bs(xml, "html.parser")
    for record in data_xml.find_all("record"):
        for ts in record.find_all("ts"):
            id_, date, time, value = record.get("id"), ts.get("date"), ts.get("time"), ts.text
            data.append(", ".join([id_, date, time, value]) + "\n")

with open("data.csv", "w") as csv:
    csv.write("ID, date, time, value\n")
    csv.writelines(data)
0 голосов
/ 17 июня 2020

Чтобы использовать l xml, вы можете просто передать строку как html (). Используя xpath // record / ts (начиная с двойной backsla sh), вы можете получить все свои результаты ts. Доступ к основному идентификатору можно получить, вызвав .getparent (), а затем атрибут.

Чтобы преобразовать xml в csv, я бы рекомендовал использовать python пакет csv. Вы можете использовать обычный файловый писатель. Однако csv write решает множество проблем, и он чище.

В общем, у вас есть один метод, который решает все. Я бы рекомендовал разбить лог c на функции. Подумайте Единственная ответственность . Также в приведенном ниже решении я преобразовал узлы xml в NamedTupple, а затем записал namedTupple в csv. Это намного проще поддерживать / читать. (т.е. есть одно место, которое задает текст заголовка, и одно место заполняет данные).

from lxml import etree
import csv #py -m pip install python-csv
import collections
from collections import namedtuple

Record = namedtuple('Record', ['id', 'date', 'time', 'value']) # Model to store records.

def CreateCsvFile(results):
    with open('results.csv', 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=list(Record._fields)) # use the namedtuple fields for the headers 
        writer.writeheader()
        writer.writerows([x._asdict() for x in results]) # To use DictWriter, the namedtuple has to be converted to dictionary

def FormatRecord(xmlNode):
    return Record(xmlNode.getparent().attrib['id'], xmlNode.attrib["date"], xmlNode.attrib["time"], xmlNode.text)

def Main(html):
    xmlTree = etree.HTML(html)
    results = [FormatRecord(xmlNode) for xmlNode in xmlTree.xpath('//record/ts')] # the double backslash will retrieve all nodes for record.
    CreateCsvFile(results)

if __name__ == '__main__':
    Main("""<record id="idOne">
            <ts date="2019-07-03" time="15:28:41.720440">5</ts>
            <ts date="2019-07-03" time="15:28:42.629959">10</ts>
            <ts date="2019-07-03" time="15:28:43.552677">15</ts>
            <ts date="2019-07-03" time="15:28:43.855345">20</ts>
        </record>

        <record id="idTwo">
            <ts date="2019-07-03" time="15:28:45.072922">30</ts>
            <ts date="2019-07-03" time="15:28:45.377087">35</ts>
            <ts date="2019-07-03" time="15:28:46.316321">40</ts>
            <ts date="2019-07-03" time="15:28:47.527960">45</ts>
        </record>""")
...