【问题标题】:Python or PETL Parsing XMLPython 或 PETL 解析 XML
【发布时间】:2021-11-02 17:49:01
【问题描述】:

我一直在玩 PETL,看看是否可以提取多个 xml 文件并将它们合并为一个。

我无法控制 XML 文件的结构,以下是我看到的变化,这给我带来了麻烦。

XML 文件 1 示例:

<?xml version="1.0" encoding="utf-8"?>
    <Export>
        <Info>
            <Name>John Doe</Name>
            <Date>01/01/2021</Date>
        </Info>
        <App>
            <Description></Description>
            <Type>Two</Type>
            <Details>
                <DetailOne>1</DetailOne>
                <DetailTwo>2</DetailTwo>
            </Details>
            <Details>
                <DetailOne>10</DetailOne>
                <DetailTwo>11</DetailTwo>
            </Details>
        </App>
    </Export>

XML 文件 2 示例:

<?xml version="1.0" encoding="utf-8"?>
    <Export>
        <Info>
            <Name></Name>
            <Date>01/02/2021</Date>
        </Info>
        <App>
            <Description>Sample description here.</Description>
            <Type>One</Type>
            <Details>
                <DetailOne>1</DetailOne>
                <DetailTwo>2</DetailTwo>
                <DetailOne>3</DetailOne>
                <DetailTwo>4</DetailTwo>
            </Details>
            <Details>
                <DetailOne>10</DetailOne>
                <DetailTwo>11</DetailTwo>
            </Details>
        </App>
    </Export>

我的 python 代码只是扫描子文件夹 xmlfiles,然后尝试使用 PETL 从那里解析。有了文档的结构,我目前正在加载三个表:

1 保存信息名称和日期 2 保存描述和类型 3 收集详情

import petl as etl
import os
from lxml import etree

for filename in os.listdir(os.getcwd() + '.\\xmlfiles\\'):
    if filename.endswith('.xml'):
        # Get the info children
        table1 = etl.fromxml((os.getcwd() + '.\\xmlfiles\\' + filename), 'Info', {
            'Name': 'Name',
            'Date': 'Date'
        })

        # Get the App children
        table2 = etl.fromxml((os.getcwd() + '.\\xmlfiles\\' + filename), 'App', {
            'Description': 'Description',
            'Type': 'Type'
        })

        # Get the App Details children
        table3 = etl.fromxml((os.getcwd() + '.\\xmlfiles\\' + filename), 'App/Details', {
            'DetailOne': 'DetailOne',
            'DetailTwo': 'DetailTwo'
        })

        # concat
        c = etl.crossjoin(table1, table2, table3)
        # I want the filename added on
        result = etl.addfield(c, 'FileName', filename)

        print('Results:\n', result)
                

我连接这三个表,因为我希望每行的 Info 和 App 数据以及每个细节。这一直有效,直到我得到一个包含多个 DetailOne 和 DetailTwo 元素的 XML 文件。

我得到的结果是:

结果:

 +------------+----------+-------------+------+-----------+-----------+----------+
| Date       | Name     | Description | Type | DetailOne | DetailTwo | FileName |
+============+==========+=============+======+===========+===========+==========+
| 01/01/2021 | John Doe | None        | Two  | 1         | 2         | one.xml  |
+------------+----------+-------------+------+-----------+-----------+----------+
| 01/01/2021 | John Doe | None        | Two  | 10        | 11        | one.xml  |
+------------+----------+-------------+------+-----------+-----------+----------+

结果:

 +------------+------+--------------------------+------+------------+------------+----------+
| Date       | Name | Description              | Type | DetailOne  | DetailTwo  | FileName |
+============+======+==========================+======+============+============+==========+
| 01/02/2021 | None | Sample description here. | One  | ('1', '3') | ('2', '4') | two.xml  |
+------------+------+--------------------------+------+------------+------------+----------+
| 01/02/2021 | None | Sample description here. | One  | 10         | 11         | two.xml  |
+------------+------+--------------------------+------+------------+------------+----------+

显示 DetailOne 为 ('1','3') 和 DetailTwo 为 ('2', '4') 的第二个文件不是我想要的。

我想要的是:

+------------+------+--------------------------+------+------------+------------+----------+
| Date       | Name | Description              | Type | DetailOne  | DetailTwo  | FileName |
+============+======+==========================+======+============+============+==========+
| 01/02/2021 | None | Sample description here. | One  | 1          | 2          | two.xml  |
+------------+------+--------------------------+------+------------+------------+----------+
| 01/02/2021 | None | Sample description here. | One  | 3          | 4          | two.xml  |
+------------+------+--------------------------+------+------------+------------+----------+
| 01/02/2021 | None | Sample description here. | One  | 10         | 11         | two.xml  |
+------------+------+--------------------------+------+------------+------------+----------+

我相信 XPath 可能是要走的路,但经过研究:

https://petl.readthedocs.io/en/stable/io.html#xml-files - 没有深入了解 lxml 和 petl

这里有一些简单的阅读: https://www.w3schools.com/xml/xpath_syntax.asp

在此处阅读更多内容: https://lxml.de/tutorial.html

感谢您对此提供任何帮助!

【问题讨论】:

  • 就我个人而言,我会选择 XSLT 来完成这项任务。有关示例,请参见 stackoverflow.com/questions/37694540/…
  • 我忘了在我的帖子中提到最终目标是将数据上传到数据库。 PETL 吸引我的是我有多种文件格式,我正试图将其编译成一个数据集。我将研究一下 XSLT,看看这对我来说是否是一个解决方案。不过,我希望看看有人可以如何使用 PETL 来实现这一点。

标签: python xml etl petl


【解决方案1】:

首先,感谢您花时间写一个好问题。我很乐意花时间回答它。

我从未使用过 PETL,但我确实扫描了文档以进行 XML 处理。我认为您的主要问题是 &lt;Details&gt; 标签有时包含一对标签,有时包含多对标签。如果只有一种方法可以提取和标签值的平面列表,而不会妨碍封闭标签......

幸好有。我使用了https://www.webtoolkitonline.com/xml-xpath-tester.html,当应用于您的示例 XML 时,XPath 表达式 //Details/DetailOne 返回列表 1,3,10

所以我怀疑这样的事情应该可以工作:

import petl as etl
import os
from lxml import etree

for filename in os.listdir(os.getcwd() + '.\\xmlfiles\\'):
    if filename.endswith('.xml'):
        # Get the info children
        table1 = etl.fromxml((os.getcwd() + '.\\xmlfiles\\' + filename), 'Info', {
            'Name': 'Name',
            'Date': 'Date'
        })

        # Get the App children
        table2 = etl.fromxml((os.getcwd() + '.\\xmlfiles\\' + filename), 'App', {
            'Description': 'Description',
            'Type': 'Type'
        })

        # Get the App Details children
        table3 = etl.fromxml((os.getcwd() + '.\\xmlfiles\\' + filename), '/App', {
            'DetailOne': '//DetailOne',
            'DetailTwo': '//DetailTwo'
        })

        # concat
        c = etl.crossjoin(table1, table2, table3)
        # I want the filename added on
        result = etl.addfield(c, 'FileName', filename)

        print('Results:\n', result)

前导 // 可能是多余的。它是“在文档中的任何级别”的 XPath 语法。我不知道 PETL 如何处理 XPath,所以我尽量安全。顺便说一句,我同意 - 文档 非常注重细节。

【讨论】:

  • 感谢您抽出宝贵时间来写这篇文章。这帮助我进入了我需要的方向。在进行了更广泛的研究后,我找到了一个对我有用的解决方案,但最终意识到我使用的 PETL 是错误的。
猜你喜欢
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-20
  • 2011-05-03
  • 1970-01-01
相关资源
最近更新 更多