You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过字典传递lxml/etree提取规则至函数?或利用pandas.read_xml的xpath参数?

问题描述

我需要从分层结构的XML文件中提取数据并导入至DataFrame。由于XML存在层级,若不编写XSLT脚本进行扁平化处理,无法直接使用便捷的pandas.read_xml()函数。考虑到XSLT会降低代码可读性,目前我采用lxml包的objectify()函数实现提取,相关代码如下:

示例XML结构

<validElementaryExchanges majorRelease="3" minorRelease="0" majorRevision="0" minorRevision="44038" xmlns="http://www.EcoInvent.org/EcoSpold02">
  <elementaryExchange id="38a622c6-f086-4763-a952-7c6b3b1c42ba" unitId="487df68b-4994-4027-8fdc-a4dc298257b7" formula="C4H10O2" casNumber="000110-63-4">
    <name xml:lang="en">1,4-Butanediol</name>
    <unitName xml:lang="en">kg</unitName>
    <compartment subcompartmentId="e8d7772c-55ca-4dd7-b605-fee5ae764578">
      <compartment xml:lang="en">air</compartment>
      <subcompartment xml:lang="en">urban air close to ground</subcompartment>
    </compartment>
    <synonym xml:lang="en">butane-1,4-diol</synonym>
    <synonym xml:lang="en">Butylene glycol</synonym>
    <property propertyId="6393c14b-db78-445d-a47b-c0cb866a1b25" amount="0" />
    <property propertyId="6d9e1462-80e3-4f10-b3f4-71febd6f1168" amount="0" />
    <property propertyId="a9358458-9724-4f03-b622-106eda248916" amount="0" />
    <property propertyId="c74c3729-e577-4081-b572-a283d2561a75" amount="0.533098393070742" />
    <property propertyId="3a0af1d6-04c3-41c6-a3da-92c4f61e0eaa" amount="1" />
    <property propertyId="67f102e2-9cb6-4d20-aa16-bf74d8a03326" amount="1" />
  </elementaryExchange>
</validElementaryExchanges>

当前实现代码

def extract_metadata(node):
    return {
        'id': node.get('id'),
        'name': node.name.text,
        'comp': node.compartment.compartment.text,
        'subcomp': node.compartment.subcompartment.text
    }

root = lxml.objectify.parse(file).getroot()
df = pd.DataFrame([extract_metadata(i) for i in root.iterchildren()])

我希望将提取逻辑封装为可接收提取规则字典的函数,示例设想如下:

dict_data_extraction = {
    'id': "node.get('id')",
    'name': "node.name.text",
    'comp': "node.compartment.compartment.text",
    'subcomp': "node.compartment.subcompartment.text"
}

df = extract_data(file, dict_data_extraction)

请问传递etree/lxml提取规则至函数的最佳方式是什么?或者是否有简洁的方法利用pandas.read_xml()函数的xpath参数实现该需求?


解决方案

一、封装可接收提取规则的安全函数

不建议用字符串形式传递提取逻辑(存在注入风险且调试困难),推荐用XPath表达式或者可调用对象作为规则,更安全且可读性更强。

方案1:使用XPath表达式规则字典

利用lxml的XPath支持,结合XML命名空间(你的XML包含xmlns="http://www.EcoInvent.org/EcoSpold02")实现:

import lxml.etree as ET
import pandas as pd

def extract_data(file_path, extraction_rules, namespaces=None):
    tree = ET.parse(file_path)
    root = tree.getroot()
    # 默认使用示例中的命名空间
    ns = namespaces or {'es': 'http://www.EcoInvent.org/EcoSpold02'}
    rows = []
    # 遍历所有目标节点
    for node in root.xpath('es:elementaryExchange', namespaces=ns):
        row = {}
        for col_name, xpath_expr in extraction_rules.items():
            # 执行XPath查询,取第一个匹配结果
            result = node.xpath(xpath_expr, namespaces=ns)
            row[col_name] = result[0] if result else None
        rows.append(row)
    return pd.DataFrame(rows)

# 定义提取规则:键为DataFrame列名,值为相对节点的XPath表达式
extraction_rules = {
    'id': '@id',
    'name': 'es:name/text()',
    'comp': 'es:compartment/es:compartment/text()',
    'subcomp': 'es:compartment/es:subcompartment/text()'
}

df = extract_data('your_file.xml', extraction_rules)

方案2:使用可调用对象规则字典

如果更习惯lxml objectify的语法,可用lambda函数作为规则值:

from lxml import objectify
import pandas as pd

def extract_data(file_path, extraction_rules):
    root = objectify.parse(file_path).getroot()
    rows = []
    for node in root.iterchildren():
        row = {}
        for col_name, extract_func in extraction_rules.items():
            row[col_name] = extract_func(node)
        rows.append(row)
    return pd.DataFrame(rows)

# 定义提取规则:键为列名,值为接收node参数的lambda函数
extraction_rules = {
    'id': lambda n: n.get('id'),
    'name': lambda n: n.name.text,
    'comp': lambda n: n.compartment.compartment.text,
    'subcomp': lambda n: n.compartment.subcompartment.text
}

df = extract_data('your_file.xml', extraction_rules)

二、直接使用pandas.read_xml实现

pandas.read_xml支持通过xpath参数指定目标节点,结合namespaces处理XML命名空间,无需XSLT即可扁平化提取数据:

import pandas as pd

# 定义命名空间
namespaces = {'es': 'http://www.EcoInvent.org/EcoSpold02'}

# 读取XML并提取字段
df = pd.read_xml(
    'your_file.xml',
    xpath='//es:elementaryExchange',
    namespaces=namespaces,
    # 列名与对应XPath表达式(相对elementaryExchange节点)
    xpath_exprs={
        'id': '@id',
        'name': 'es:name/text()',
        'comp': 'es:compartment/es:compartment/text()',
        'subcomp': 'es:compartment/es:subcompartment/text()'
    }
)

这个方法最简洁,直接利用pandas内置功能,无需额外封装函数,同时避免了XSLT的复杂性。

内容的提问来源于stack exchange,提问作者Wasserwaage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 03:05:19