csv 有没有一个脚本可以将XML文件转换为Excel,而不使用PHP也不使用Excel的导入数据?

yhqotfr8  于 2023-07-31  发布在  PHP
关注(0)|答案(1)|浏览(74)

已经尝试使用以下代码使用Python,但XLS或CSV中的输出数据与XML中观察到的数据(通过XML记事本)不同。任何支持都将不胜感激...感谢:

import xml.etree.ElementTree as ET import pandas as pd

def parse_interval_reading(reading):
    return {
        'duration': reading.find('{http://test.test}timePeriod/{http://test.test}duration').text,
        'start': reading.find('{http://test.test}timePeriod/{http://test.test}start').text,
        'value': reading.find('{http://test.test}value').text,
    } def green_button_xml_to_df(xml_file):
    tree = ET.parse(xml_file)
    root = tree.getroot()

    readings = root.findall('.//{http://test.test}IntervalReading')

    data = [parse_interval_reading(reading) for reading in readings]
    df = pd.DataFrame(data)

    df['start'] = pd.to_datetime(df['start'], unit='s')

    return df

def convert_green_button_xml_to_csv(xml_file, csv_file):
    df = green_button_xml_to_df(xml_file)
    df.to_csv(csv_file, index=False)

convert_test_xml_to_csv('/Users/dummyname/Downloads/test.xml',
'/Users/dummyname/output.csv’)

字符串

7bsow1i6

7bsow1i61#

就像你开始时一样,你可以使用Python的标准etree模块,沿着标准csv模块,来获得一个相当简单的程序,可以像这样读取XML:

<root xmlns:test="http://test.test">
    <test:IntervalReading>
        <test:timePeriod>
            <test:duration>Dur A</test:duration>
            <test:start>Start A</test:start>
        </test:timePeriod>
        <test:value>Value A</test:value>
    </test:IntervalReading>
    <test:IntervalReading>
        <test:timePeriod>
            <test:duration>Dur B</test:duration>
            <test:start>Start B</test:start>
        </test:timePeriod>
        <test:value>Value B</test:value>
    </test:IntervalReading>
</root>

字符串
并生成CSV,如下所示:

| interval | duration | start   | value   |
|----------|----------|---------|---------|
| 1        | Dur A    | Start A | Value A |
| 2        | Dur B    | Start B | Value B |


在处理具有名称空间的XML时,提前在dict中声明名称空间,并在所有对find/findall的调用中引用该dict可以保存大量的混乱,例如:

ns = {"t": "http://test.test"}

intervals = root.findall(".//t:IntervalReading", ns)
...
interval.find("t:timePeriod", ns)


findall和find将在提供的dict ns中查找t键,然后将URL值用于所有节点评估。
请注意,dict中的t与XML源代码中的文字前缀test不同(我在上面展示过)。你也可以为ns字典选择键FooBar,然后在你的路径中引用它,比如:

intervals = root.findall(".//FooBar:IntervalReading", ns)
...
interval.find("FooBar:timePeriod", ns)


为了避免pandas(在我看来,这对于这个用例来说太多了),您可以使用csv模块中的DictWriter将dicts列表转换为CSV。这将与您已经开始的将每个interval转换为dict的方法一起工作:

rows: list[dict[str, str]] = []
...
   rows.append(
        {
            "duration": dur,
            "start": start,
            "value": value,
        }
    )
...


DictWriter需要知道它将在dicts中查找的字段名,所以我传入第一行(它知道读取dict的键),然后在写入所有行之前调用writer的writeheader方法:

...
writer = csv.DictWriter(f, fieldnames=row[0])
writer.writeheader()
write.writerows(rows)


我喜欢类型提示和预先声明我的列表和dict类型并运行Pylance,ET的find方法和text属性。元素可以返回None,所以下面的代码有很多可以省略的None检查:

import csv
import xml.etree.ElementTree as ET

ns = {"t": "http://test.test"}

root = ET.parse("input.xml")

intervals = root.findall(".//t:IntervalReading", ns)
if not intervals:
    print("error: no intervals found; check namespace/prefix and path")
    exit(1)

rows: list[dict[str, str]] = []
for i, interval in enumerate(intervals, start=1):
    value = ""
    e = interval.find("t:value", ns)
    if e is not None and e.text is not None:
        value = e.text

    dur = ""
    start = ""
    tp = interval.find("t:timePeriod", ns)
    if tp is not None:
        e = tp.find("t:duration", ns)
        if e is not None and e.text is not None:
            dur = e.text

        e = tp.find("t:start", ns)
        if e is not None and e.text is not None:
            start = e.text

    rows.append(
        {
            "interval": str(i),
            "duration": dur,
            "start": start,
            "value": value,
        }
    )

with open("output.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=rows[0])
    writer.writeheader()
    writer.writerows(rows)

相关问题