Python 提取子标签不一致的 XML 数据

Posted

技术标签:

【中文标题】Python 提取子标签不一致的 XML 数据【英文标题】:Python extract XML data with inconsistant child tags 【发布时间】:2022-01-17 23:15:16 【问题描述】:

我有一个 XML 文件,我需要从中提取数据并将其插入到数据库表中。我的困难是 XML 数据结构可能包含不一致的子标签。这意味着(在下面的示例中)一个父 <Field> 标签可能包含也可能不包含 <ListValue> 标签。

这是一个简短的示例,我将添加额外的 <Field> 标签,可能包含另一个 <ListValue> 标签。注意:所有<Field> 标签都应保持在<Record> 标签下方的同一级别。

我想看看是否有人有比我下面的示例更“pythonic”的方式来转换这些数据。也许有列表理解?

我需要在数据库中插入多达 4,000,000 个<Record> 级别的数据行,因此我不想浪费更多的时间来循环遍历 XML。速度至关重要。

我们将不胜感激。

<?xml version="1.0" encoding="utf-16"?>
<Records count="10">
    <Metadata>
        <FieldDefinitions>
            <FieldDefinition id="15084" guid="f3426157-cbcb-4293-94e5-9f1c993db4b5" name="CCR_ID" alias="CCR_ID" />
            <FieldDefinition id="16335" guid="5dfddb49-9a7a-46ee-9bd2-d5bbed97a48d" name="Coming Due" alias="Coming_Due" />
        </FieldDefinitions>
    </Metadata>
    <LevelCounts>
        <LevelCount id="35" guid="661c747f-7ce5-474a-b320-044aaec7a5b1" count="10" />
    </LevelCounts>
    <Record contentId="20196771" levelId="35" levelGuid="661c747f-7ce5-474a-b320-044aaec7a5b1" moduleId="265" parentId="0">
        <Field id="15084" guid="f3426157-cbcb-4293-94e5-9f1c993db4b5" type="1">100383-320-V0217111</Field>
        <Field id="16335" guid="5dfddb49-9a7a-46ee-9bd2-d5bbed97a48d" type="4">
            <ListValues>
                <ListValue id="136572" displayName="121 - 180 days out">121 - 180 days out</ListValue>
            </ListValues>
        </Field>
    </Record>
    <Record contentId="20205193" levelId="35" levelGuid="661c747f-7ce5-474a-b320-044aaec7a5b1" moduleId="265" parentId="0">
        <Field id="15084" guid="f3426157-cbcb-4293-94e5-9f1c993db4b5" type="1">100383-320-V0217267</Field>
        <Field id="16335" guid="5dfddb49-9a7a-46ee-9bd2-d5bbed97a48d" type="4">
            <ListValues>
                <ListValue id="136572" displayName="121 - 180 days out">121 - 180 days out</ListValue>
            </ListValues>
        </Field>
    </Record>
    <Record contentId="20196779" levelId="35" levelGuid="661c747f-7ce5-474a-b320-044aaec7a5b1" moduleId="265" parentId="0">
        <Field id="15084" guid="f3426157-cbcb-4293-94e5-9f1c993db4b5" type="1">100384-320-V0217111</Field>
        <Field id="16335" guid="5dfddb49-9a7a-46ee-9bd2-d5bbed97a48d" type="4">
            <ListValues>
                <ListValue id="136572" displayName="121 - 180 days out">121 - 180 days out</ListValue>
            </ListValues>
        </Field>
    </Record>
</Records>

这是我解析数据的代码:

from xml.etree import ElementTree
import pandas as pd

xml_string = '''SEE STRING ABOVE'''

auth_token = ElementTree.fromstring(xml_string.text)

dct = []
cols = ['CCR_ID', 'Coming_Due']

for r in auth_token.findall("Record"):
    for f in r.findall("Field"):

        if f.attrib['id'] == '15084':
            ccr_id = f.text

        for l in f.findall(".//ListValue"):
            coming_due = l.text

    dct.append((ccr_id, coming_due))


df = pd.DataFrame(dct)
df.columns = cols

print(df)

这是我的结果:

                CCR_ID Coming_Due
0  100383-320-V0217111    121 - 180 days out
1  100383-320-V0217267    121 - 180 days out
2  100384-320-V0217111    121 - 180 days out
3  100384-320-V0217267    121 - 180 days out
4  100681-320-V0217111    121 - 180 days out
5  100681-320-V0217267      11 - 30 days out
6  100684-320-V0217111    121 - 180 days out
7  100684-320-V0217267      11 - 30 days out
8  100685-320-V0217111    121 - 180 days out
9  100685-320-V0217267      11 - 30 days out

【问题讨论】:

也许您可以在循环之前创建数据框并将记录直接附加到其中。 那么结果有什么问题呢?您对示例 xml 的预期输出究竟是什么? @JackFleeting 我的结果似乎没有任何问题,我正在显示正确的数据,我试图确定是否有比从 XML 收集信息更有效的方法使用多个 FOR 循环。 拥有 400 万条记录标签,您应该使用 iterparse 来避免读取内存中的整个文档。 【参考方案1】:

如果我理解正确,使用 pandas read_xml() 可能会有所帮助:

df = pd.read_xml(string,"//Record//*")
df2= df[['Field','displayName']].copy()
df2['displayName'] = df2['displayName'].shift(-3)
df2.set_axis(['CCR_ID', 'Coming_Due'], axis=1,inplace=True)
df2.dropna()

基于您的示例 xml 的输出:

    Field   displayName
0   100383-320-V0217111     121 - 180 days out
4   100383-320-V0217267     121 - 180 days out
8   100384-320-V0217111     121 - 180 days out

【讨论】:

我尝试使用 Pandas read_xml() 但收到导入错误消息。我在虚拟环境中安装了 pandas 1.3.4 并激活了该环境。我还在其中一个模块中找到了 read_xml() 函数,但它未被识别。我正在将 Pandas 升级到 1.3.5 以查看是否可以解决问题。我将测试您的解决方案,这已完成。 感谢您的建议。我没有尝试在标签之后使用 //* 读取 xml。这使我能够收集所有必需的信息。只需对 dropna() df2.dropna(inplace=True 进行一次更改,否则 dropna() 返回一个单独的数据帧。此外,为了使用 read_xml(),我必须安装 lxml。也许我在文档中错过了它,所以我想把它放在这里以防其他人遇到同样的问题。 pip install lxml pandas

以上是关于Python 提取子标签不一致的 XML 数据的主要内容,如果未能解决你的问题,请参考以下文章

使用python提取特定的xml标签值

Python:如果XML标签不存在,我需要打印'Blank'和Output

Python爬虫教程-24-数据提取-BeautifulSoup4

使用 Python Etree 解析 XML 并返回指定的标签而不考虑命名空间

RaptureXML 无法访问子标签

从值标签Etree XML python中提取文本