从多个 XML 节点中提取值 [重复]

Posted

技术标签:

【中文标题】从多个 XML 节点中提取值 [重复]【英文标题】:Extract values from multiple XML nodes [duplicate] 【发布时间】:2020-05-05 04:35:23 【问题描述】:

我有如下数据结构(原来是2.5gb,所以必须解析):

<households xmlns="http://www.matsim.org/files/dtd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.matsim.org/files/dtd http://www.matsim.org/files/dtd/households_v1.0.xsd">
    <household id="1473">
        <members>
            <personId refId="2714"/>
            <personId refId="2715"/>
            <personId refId="2716"/>
            <personId refId="2717"/>
            <personId refId="2718"/>
            <personId refId="2719"/>
        </members>
        <income currency="CHF" period="month">
                3094.87101
        </income>
        <attributes>
            <attribute name="bikeAvailability" class="java.lang.String" >some</attribute>
            <attribute name="carAvailability" class="java.lang.String" >some</attribute>
            <attribute name="consumptionUnits" class="java.lang.Double" >3.3</attribute>
            <attribute name="householdIncomePerConsumptionUnit" class="java.lang.Double" >3094.8710104279835</attribute>
            <attribute name="numberOfCars" class="java.lang.Integer" >1</attribute>
            <attribute name="residenceZoneCategory" class="java.lang.Integer" >1</attribute>
            <attribute name="totalHouseholdIncome" class="java.lang.Double" >10213.074334412346</attribute>
        </attributes>

    </household>
    <household id="2474">
        <members>
            <personId refId="4647"/>
            <personId refId="4648"/>
            <personId refId="4649"/>
            <personId refId="4650"/>
            <personId refId="4651"/>
            <personId refId="4652"/>
            <personId refId="4653"/>
            <personId refId="4654"/>
            <personId refId="4655"/>
        </members>
        <income currency="CHF" period="month">
                1602.562822
        </income>
        <attributes>
            <attribute name="bikeAvailability" class="java.lang.String" >none</attribute>
            <attribute name="carAvailability" class="java.lang.String" >all</attribute>
            <attribute name="consumptionUnits" class="java.lang.Double" >3.6999999999999997</attribute>
            <attribute name="householdIncomePerConsumptionUnit" class="java.lang.Double" >1602.5628215679633</attribute>
            <attribute name="numberOfCars" class="java.lang.Integer" >1</attribute>
            <attribute name="residenceZoneCategory" class="java.lang.Integer" >1</attribute>
            <attribute name="totalHouseholdIncome" class="java.lang.Double" >5929.482439801463</attribute>
        </attributes>

    </household>
    <household id="4024">
        <members>
            <personId refId="7685"/>
        </members>
        <income currency="CHF" period="month">
                61610.096619
        </income>
        <attributes>
            <attribute name="bikeAvailability" class="java.lang.String" >none</attribute>
            <attribute name="carAvailability" class="java.lang.String" >none</attribute>
            <attribute name="consumptionUnits" class="java.lang.Double" >1.0</attribute>
            <attribute name="householdIncomePerConsumptionUnit" class="java.lang.Double" >61610.096618936936</attribute>
            <attribute name="numberOfCars" class="java.lang.Integer" >0</attribute>
            <attribute name="residenceZoneCategory" class="java.lang.Integer" >1</attribute>
            <attribute name="totalHouseholdIncome" class="java.lang.Double" >61610.096618936936</attribute>
        </attributes>

    </household>
</households>

我想提取所有person ID refId 值及其对应的income 值。最终,我计划有一个包含 personId 列和收入列的 df(收入将是重复的)。所以棘手的部分不仅是命名空间,还有如何在不同的节点级别访问 XML。

到目前为止,我的方法未能做到这一点。

import gzip
import xml.etree.ElementTree as ET
from collections import defaultdict
import pandas as pd
import numpy as np

tree = ET.parse(gzip.open('V0_1pm/output_households.xml.gz', 'r'))
root = tree.getroot()
rows = []
for it in root.iter('household'):
    hh = it.attrib['id']
    inc = it.find('income').text
    rows.append([hh,inc])

hh_inc = pd.DataFrame(rows, columns=['id', 'PTSubscription'])
hh_inc

非常感谢任何帮助。

【问题讨论】:

"因此必须解析"你是什么意思? 你的方法是如何失败的? 我会支持其他 cmets 的要求。这里真的没什么可做的。 您的 xml 有一个命名空间 - Parsing XML with Namespaces。 root.iter('http://www.matsim.org/files/dtdhousehold') @mzjn 我的意思是,如果没有这种方法,我的计算机没有计算能力来提取信息。 【参考方案1】:

您的代码失败的原因是您的输入元素具有非空命名空间。

处理命名空间 XML 的方法之一是:

定义一个字典“快捷方式:命名空间”,包含所有使用的命名空间 在您的 XPath 表达式中。 调用 findallfind,将此字典作为第二个参数传递 并在其中添加相关的命名空间快捷方式(和冒号作为分隔符) XPath 表达式。

还要注意 find(...).text 返回 full 文本,带有 newline 字符 和空格。为了解决这个问题,您可能应该:

从“周围的”白色字符中去除读取的内容。 将其转换为 float

所以把你的代码改成:

# Namespace dictionary
ns = 'dtd': 'http://www.matsim.org/files/dtd'
rows = []
for it in root.findall('dtd:household', ns):
    hh = it.attrib['id']
    inc = it.find('dtd:income', ns).text
    inc = float(inc.strip())
    rows.append([hh, inc])
hh_inc = pd.DataFrame(rows, columns=['id', 'PTSubscription'])
hh_inc

对于您的示例输入,我得到了:

     id  PTSubscription
0  1473     3094.871010
1  2474     1602.562822
2  4024    61610.096619

按照关于 refId 的问题进行编辑

我假设 DataFrame 应该为每个 refId 包含单独的行, 具有相关的 idPTSubscription

要包含 refId,请将循环更改为:

for it in root.findall('dtd:household', ns):
    hh = it.attrib['id']
    inc = it.find('dtd:income', ns).text
    inc = float(inc.strip())
    pids = it.findall('.//dtd:personId', ns)
    for pId in pids:
        refId = pId.attrib['refId']
        rows.append([hh, inc, int(refId)])
    if not pids:
        rows.append([hh, inc, -1])

我添加了最后 2 条指令,以免“丢失”任何家庭 不包含 refId

创建DataFrame时,传递额外的列名:

hh_inc = pd.DataFrame(rows, columns=['id', 'PTSubscription', 'refId'])

【讨论】:

非常感谢您的工作!它确实有效。不幸的是,在我开始的代码中,我只使用了家庭 ID。我真正想要的是住在家里的人的refId。但是,我不确定如何调整此代码,因为它位于收入之外的另一个节点内。

以上是关于从多个 XML 节点中提取值 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

从 Clob 列中提取 XML 标记值,在 Oracle 中具有多个具有相同名称的标记

从 C# 中的 xml 文件中选择多个值

从多个 XML 行中提取数据

在 Python 中使用正则表达式从特定 xml 标记中提取特定值 [重复]

检索多个 xml 子节点值

如何获取xml某个节点的多个值