使用 Python lxml 解析带有条件的 ONIX xml

Posted

技术标签:

【中文标题】使用 Python lxml 解析带有条件的 ONIX xml【英文标题】:Parsing an ONIX xml with conditions using Python lxml 【发布时间】:2022-01-20 17:06:40 【问题描述】:

我正在尝试使用 Python lxml 解析器从 ONIX XML format 文件中提取一些信息。

除此之外,我对文档感兴趣的部分如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<ProductSupply>
       <SupplyDetail>
          <Supplier>
             <SupplierRole>03</SupplierRole>
             <SupplierName>EGEN</SupplierName>
          </Supplier>
          <ProductAvailability>40</ProductAvailability>
          <Price>
             <PriceType>01</PriceType>
             <PriceAmount>0.00</PriceAmount>
             <Tax>
                <TaxType>01</TaxType>
                <TaxRateCode>Z</TaxRateCode>
                <TaxRatePercent>0</TaxRatePercent>
                <TaxableAmount>0.00</TaxableAmount>
                <TaxAmount>0.00</TaxAmount>
             </Tax>
             <CurrencyCode>NOK</CurrencyCode>
          </Price>
          <Price>
             <PriceType>02</PriceType>
             <PriceQualifier>05</PriceQualifier>
             <PriceAmount>0.00</PriceAmount>
             <Tax>
                <TaxType>01</TaxType>
                <TaxRateCode>Z</TaxRateCode>
                <TaxRatePercent>0</TaxRatePercent>
                <TaxableAmount>0.00</TaxableAmount>
                <TaxAmount>0.00</TaxAmount>
             </Tax>
             <CurrencyCode>NOK</CurrencyCode>
          </Price>
       </SupplyDetail>
    </ProductSupply>

我需要在以下条件下领取价格金额

PriceType='02' and CurrencyCode='NOK' and PriceQualifier='05'

我试过了:

price = p.find(
"ProductSupply/SupplyDetail[Supplier/SupplierRole='03']/Price[PriceType='02' \
and CurrencyCode='NOK' and PriceQualifier='05']/PriceAmount").text

由于某种原因,我的带有 and 运算符的 XPath 无法正常工作并出现以下错误:

File "<string>", line unknown
    SyntaxError: invalid predicate

知道如何处理它吗? 非常感谢任何帮助!

【问题讨论】:

尝试使用.xpath() 而不是.find() 【参考方案1】:

TL;DR:使用xpath(),因为find*() 方法不支持像and 这样的布尔运算符。


作为Daniel suggested,您应该使用lxml 的解析器方法xpath() 来处理(相当复杂的)XPath 表达式。

XPath

您的 XPath 表达式包含 节点测试predicates,它们使用 boolean operator and (XPath 1.0):

ProductSupply/SupplyDetail[Supplier/SupplierRole='03']/Price[PriceType='02' \
and CurrencyCode='NOK' and PriceQualifier='05']/PriceAmount

提示:在线测试(见Xpather demo)。这断言它按预期找到了单个元素 &lt;PriceAmount&gt;0.00&lt;/PriceAmount&gt;

使用find() 方法

根据 Python 文档,您可以使用以下接受匹配表达式(例如 XPath)作为参数的 find 方法:

    find findAll

问题:对find() 的 XPath 语法支持有限

虽然他们的supported XPath syntax是有限的!

限制包括逻辑运算符,例如您的 and。 Karl Thornton 在他的页面 XML parsing: Python ~ XPath ~ logical AND | Shiori 上对此进行了解释。

另一方面,note on lxml documentation 更喜欢他们:

.find*() 方法通常比成熟的 XPath 支持更快。它们还通过 .iterfind() 方法支持增量树处理,而 XPath 总是在返回它们之前收集所有结果。因此,当不需要高度选择性的 XPath 查询时,出于速度和内存方面的考虑,建议使用它们而不是 XPath。

(强调我的)

使用lxml的xpath()

让我们从更安全、更丰富的xpath() 函数开始(在过早优化之前)。例如:

# the node predicates to apply within XPath
sd_predicate = "[Supplier/SupplierRole='03']"
p_predicate = "[PriceType='02' and CurrencyCode='NOK' and PriceQualifier='05']"

pa_xpath = f"ProductSupply/SupplyDetailsd_predicate/Pricep_predicate/PriceAmount"  # building XPath including predicates with f-string
print("Using XPath:", pa_xpath) # remove after debugging

root = tree.getroot()
price_amount = root.xpath(pa_xpath)
print("XPath evaluated to:", price_amount) # remove after debugging

另见:

官方lxml指南:XPath and XSLT with lxml Using XPath in Python with LXML

【讨论】:

哇,谢谢你的详细解答!

以上是关于使用 Python lxml 解析带有条件的 ONIX xml的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中使用 XPath 和 LXML

学习笔记Python - Lxml

Python爬虫编程思想(39):使用lxml解析HTML与XML

Python 之lxml解析模块

使用 Lxml 解析 HTML

使用 python 解析 HTML 表 - HTMLparser 或 lxml