【问题标题】:Iterate XPath elements to get individual elements instead of list迭代 XPath 元素以获取单个元素而不是列表
【发布时间】:2019-04-18 13:08:24
【问题描述】:

我正在解析一个 XML 文档并使用 XPath 读取不同元素的值。目前,这可以很好地获取列表中的所有元素。 但是,子元素并不总是存在于所有父元素中(但存在于某些父元素中!),我需要知道在解析 xml 以创建要插入数据库的数据框时是哪个元素。 所以我想遍历元素并一次获取我需要的值。我不确定如何执行此操作,因为目前我在每次迭代中都获得了完整列表。 我正在提取嵌套在不同级别的元素。

我正在解析的 xml 是 Garmin 的 TCX 文件。简短的例子:

 <?xml version="1.0" encoding="UTF-8"?>
<TrainingCenterDatabase
  xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd"
  xmlns:ns5="http://www.garmin.com/xmlschemas/ActivityGoals/v1"
  xmlns:ns3="http://www.garmin.com/xmlschemas/ActivityExtension/v2"
  xmlns:ns2="http://www.garmin.com/xmlschemas/UserProfile/v2"
  xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns4="http://www.garmin.com/xmlschemas/ProfileExtension/v1">
  <Activities>
    <Activity Sport="Running">
      <Id>2018-10-10T14:10:10.000Z</Id>
      <Lap StartTime="2018-10-10T14:10:10.000Z">
        <TotalTimeSeconds>343.0</TotalTimeSeconds>
        <DistanceMeters>1000.0</DistanceMeters>
        <MaximumSpeed>3.694999933242798</MaximumSpeed>
        <Calories>51</Calories>
        <AverageHeartRateBpm>
          <Value>136</Value>
        </AverageHeartRateBpm>
        <MaximumHeartRateBpm>
          <Value>162</Value>
        </MaximumHeartRateBpm>
        <Intensity>Active</Intensity>
        <TriggerMethod>Manual</TriggerMethod>
        <Track>
          <Trackpoint>
            <Time>2018-10-10T14:10:10.000Z</Time>
            <Position>
              <LatitudeDegrees>52.17917550355196</LatitudeDegrees>
              <LongitudeDegrees>6.532441098242998</LongitudeDegrees>
            </Position>
            <AltitudeMeters>-0.20000000298023224</AltitudeMeters>
            <DistanceMeters>0.0</DistanceMeters>
            <HeartRateBpm>
              <Value>94</Value>
            </HeartRateBpm>
            <Extensions>
              <ns3:TPX>
                <ns3:Speed>0.04699999839067459</ns3:Speed>
                <ns3:RunCadence>7</ns3:RunCadence>
              </ns3:TPX>
            </Extensions>
          </Trackpoint>
          <Trackpoint>
            <Time>2018-10-10T14:10:11.000Z</Time>
            <Position>
              <LatitudeDegrees>52.17917634174228</LatitudeDegrees>
              <LongitudeDegrees>6.532444199547172</LongitudeDegrees>
            </Position>
            <AltitudeMeters>0.0</AltitudeMeters>
            <DistanceMeters>0.23000000417232513</DistanceMeters>
            <HeartRateBpm>
              <Value>95</Value>
            </HeartRateBpm>
            <Extensions>
              <ns3:TPX>
                <ns3:Speed>0.0</ns3:Speed>
                <ns3:RunCadence>7</ns3:RunCadence>
              </ns3:TPX>
            </Extensions>
          </Trackpoint>
          <Trackpoint>
            <Time>2018-10-10T14:10:12.000Z</Time>
            <Position>
              <LatitudeDegrees>52.17917206697166</LatitudeDegrees>
              <LongitudeDegrees>6.532468926161528</LongitudeDegrees>
            </Position>
            <AltitudeMeters>0.0</AltitudeMeters>
            <DistanceMeters>1.9700000286102295</DistanceMeters>
            <Extensions>
              <ns3:TPX>
                <ns3:Speed>0.0</ns3:Speed>
                <ns3:RunCadence>7</ns3:RunCadence>
              </ns3:TPX>
            </Extensions>
          </Trackpoint>
          <Trackpoint>
            <Time>2018-10-10T14:10:13.000Z</Time>
            <Position>
              <LatitudeDegrees>52.17916024848819</LatitudeDegrees>
              <LongitudeDegrees>6.5325202234089375</LongitudeDegrees>
            </Position>
            <AltitudeMeters>0.0</AltitudeMeters>
            <DistanceMeters>5.679999828338623</DistanceMeters>
            <HeartRateBpm>
              <Value>96</Value>
            </HeartRateBpm>
            <Extensions>
              <ns3:TPX>
                <ns3:Speed>0.08399999886751175</ns3:Speed>
                <ns3:RunCadence>7</ns3:RunCadence>
              </ns3:TPX>
            </Extensions>
          </Trackpoint>
          <Trackpoint>
            <Time>2018-10-10T14:10:14.000Z</Time>
            <Position>
              <LatitudeDegrees>52.17914817854762</LatitudeDegrees>
              <LongitudeDegrees>6.532532041892409</LongitudeDegrees>
            </Position>
            <AltitudeMeters>0.0</AltitudeMeters>
            <DistanceMeters>7.150000095367432</DistanceMeters>
            <HeartRateBpm>
              <Value>98</Value>
            </HeartRateBpm>
            <Extensions>
              <ns3:TPX>
                <ns3:Speed>0.10300000011920929</ns3:Speed>
                <ns3:RunCadence>10</ns3:RunCadence>
              </ns3:TPX>
            </Extensions>
          </Trackpoint>

将文件中的所有值作为列表提供给我的有效代码:

from lxml import etree, objectify
from os import listdir
from os.path import isfile, join

def tcxParse(tcxFile):
    parser = etree.XMLParser(remove_blank_text=True)
    tree = etree.parse(tcxFile, parser)
    root = tree.getroot()

    ####
    #strip namespaces
    for elem in root.getiterator():
        if not hasattr(elem.tag, 'find'): continue  # (1)
        i = elem.tag.find('}')
        if i >= 0:
            elem.tag = elem.tag[i + 1:]
    objectify.deannotate(root, cleanup_namespaces=True)
    ####
#check if we are dealing with .tcx or other format
    if tcxFile.lower().endswith('.tcx'):
        tcxParse.activity = tree.xpath('//*[@Sport]/@Sport')
        tcxParse.HR = list(map(int, tree.xpath('//Track/Trackpoint/HeartRateBpm/Value/text()')))
        tcxParse.Time = tree.xpath('//Time/text()')
        tcxParse.Speed = list(map(float, tree.xpath('//Track/Trackpoint/Extensions/TPX/Speed/text()')))
        tcxParse.Cadence = list(map(int, tree.xpath('//Track/Trackpoint/Extensions/TPX/RunCadence/text()')))
        tcxParse.Lat = list(map(float, tree.xpath('//Track/Trackpoint/Position/LatitudeDegrees/text()')))
        tcxParse.Lon = list(map(float, tree.xpath('//Track/Trackpoint/Position/LongitudeDegrees/text()')))
        tcxParse.Alt = list(map(float, tree.xpath('//Track/Trackpoint/AltitudeMeters/text()')))
        tcxParse.Distance = list(map(float, tree.xpath('//Track/Trackpoint/DistanceMeters/text()')))

我知道我可以使用 tree.iter() 来迭代元素,但不确定如何一次获取一个值而不是完整列表。

要明确: 例如,tcxParse.HR 的当前输出为:

94,95,96,98

但我需要它

94,95,nan,96,98 

因为第三个 Trackpoint 元素中缺少 HeartRateBpm

【问题讨论】:

  • 你当前的输出和期望输出是什么?
  • 根据我在此处包含的 sn-p 将其添加到问题中。希望这样清楚
  • 我认为你需要用 python 来做这件事。我不确定,但似乎使用 XPath 1.0 您将无法获得 nan 缺少的元素。你可以检查这个答案[,但正如我所说,它可能不适用于数组结果]:stackoverflow.com/a/4490667/7128891

标签: python xml xpath


【解决方案1】:

据我了解,您需要在 &lt;Track&gt; 中迭代 &lt;Trackpoint&gt;
我建议你这样做:

trackpoints = [{
    'HR': tp.findtext('HeartRateBpm/Value'),
    'Time': tp.findtext('Time'),
    'Speed': tp.findtext('Extensions/TPX/Speed'),
    'Cadence': tp.findtext('Extensions/TPX/RunCadence'),
    'Lat': tp.findtext('Position/LatitudeDegrees'),
    'Lon': tp.findtext('Position/LongitudeDegrees'),
    'Alt': tp.findtext('AltitudeMeters'),
    'Distance': tp.findtext('DistanceMeters')
    }
for tp in tree.xpath('//Track/Trackpoint')]

对于有问题的 xml 块(在第二个 &lt;Trackpoint&gt; 中删除了 &lt;HeartRateBpm&gt;) - trackpoints 将包含这样的列表:

[{'HR': '94', 'Time': '2018-10-10T14:10:10.000Z', 'Speed': '0.04699999839067459', 'Cadence': '7', 'Lat': '52.17917550355196', 'Lon': '6.532441098242998', 'Alt': '-0.20000000298023224', 'Distance': '0.0'}, 
 {'HR': None, 'Time': '2018-10-10T14:10:11.000Z', 'Speed': '0.0', 'Cadence': '7', 'Lat': '52.17917634174228', 'Lon': '6.532444199547172', 'Alt': '0.0', 'Distance': '0.23000000417232513'}]

【讨论】:

  • 谢谢,这正是我所需要的!
猜你喜欢
  • 1970-01-01
  • 2011-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-24
  • 2011-10-06
  • 1970-01-01
相关资源
最近更新 更多