【问题标题】:python xml xpath query using tag and attribute with nspython xml xpath查询使用带有ns的标签和属性
【发布时间】:2014-03-17 20:18:38
【问题描述】:

我一定是在这里做错了什么,我在 SO 上看到和搜索的每个示例似乎都表明这是可行的。

我正在尝试使用带有 lxml etree 库的 XPath 搜索来解析 garmin tcx 文件:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd">

  <Workouts>
    <Workout Sport="Biking">
      <Name>3P2 WK16 - 3</Name>
      <Step xsi:type="Step_t">
        <StepId>1</StepId>
        <Name>[MP19]6:28-6:38</Name>
        <Duration xsi:type="Distance_t">
          <Meters>13000</Meters>
        </Duration>
        <Intensity>Active</Intensity>
        <Target xsi:type="Speed_t">
          <SpeedZone xsi:type="PredefinedSpeedZone_t">
            <Number>2</Number>
          </SpeedZone>
        </Target>
      </Step>
     ......
     </Workout>
</Workouts>
</TrainingCenterDatabase>

我只想返回类型为 PredefinedSpeedZone_t 的 SpeedZone 元素。我以为我能做到:

root = ET.parse(open('file.tcx'))
xsi = {'xsi': 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2'}
    for speed_zone in root.xpath(".//xsi:SpeedZone[@xsi:type='PredefinedSpeedZone_t']", namespaces=xsi):
        print speed_zone

虽然情况似乎并非如此。我尝试了很多删除/添加命名空间的组合,但无济于事。如果我删除属性搜索并将其保留为 ".//xsi:SpeedZone" 则返回:

<Element {http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}SpeedZone at 0x2595188>

正如我所料。

我想我可以在 for 循环中做到这一点,但感觉应该可以在一行中完成!

【问题讨论】:

    标签: python xml xpath xml-parsing lxml


    【解决方案1】:

    我有点晚了,但其他答案令人困惑,恕我直言。

    在问题的 Python 代码和其他两个答案中,xsi 前缀绑定到 http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 URI。但在带有 Garmin 数据的 XML 文档中,xsi 绑定到 http://www.w3.org/2001/XMLSchema-instance

    由于这里有两个名称空间在起作用,我认为下面的代码可以更清楚地了解正在发生的事情。与 tcd 前缀关联的命名空间是默认命名空间。

    from lxml import etree
    
    NSMAP = {"tcd": "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2",
             "xsi": "http://www.w3.org/2001/XMLSchema-instance"}
    
    root = etree.parse('file.tcx')
    
    for speed_zone in root.xpath(".//tcd:SpeedZone[@xsi:type='PredefinedSpeedZone_t']",
                                 namespaces=NSMAP):
        print speed_zone
    

    输出:

    <Element {http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}SpeedZone at 0x25b7e18>
    

    【讨论】:

    • 我仍然不得不重新阅读您的答案 3 或 4 次,但一旦点击,它看起来就很明显了。不知道为什么我不能早点发现。认为这值得正确的答案,所以编辑它。
    【解决方案2】:

    解决此问题的一种方法是避免指定属性名称并使用*

    .//xsi:SpeedZone[@*='PredefinedSpeedZone_t']
    

    另一个选择(不像前一个那么棒)是实际获取所有SpeedZone 标签并检查循环中的属性值:

    attribute_name = '{%s}type' % root.nsmap['xsi']
    for speed_zone in root.xpath(".//xsi:SpeedZone", namespaces=xsi):
        if speed_zone.attrib.get(attribute_name) == 'PredefinedSpeedZone_t':
            print speed_zone
    

    希望对您有所帮助。

    【讨论】:

    • 谢谢,通配符属性完成了这项工作,并为我的 OCD 提供了它所需要的一个衬里 :) 我想这在 XPath 和 etree 支持之间有点奇怪。
    【解决方案3】:

    如果一切都失败了,你仍然可以使用

    ".//xsi:SpeedZone[@*[name() = 'xsi:type' and . = 'PredefinedSpeedZone_t']]"
    

    使用name()不如直接寻址命名空间属性好,但至少etree理解它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-29
      • 1970-01-01
      • 2015-03-15
      • 1970-01-01
      相关资源
      最近更新 更多