Итерация элементов XPath для получения отдельных элементов вместо списка - PullRequest
0 голосов
/ 15 ноября 2018

Я анализирую XML-документ и читаю значения различных элементов, используя XPath. В настоящее время это хорошо работает, чтобы получить все элементы в списках. Тем не менее, дочерние элементы не всегда присутствуют для всех родителей (но присутствуют у некоторых!), И мне нужно знать, какой при разборе xml создать фрейм данных для вставки в базу данных. Поэтому я хочу перебрать элементы и получить нужные мне значения по одному. Я не уверен, как это сделать, так как в настоящее время я получаю полный список на каждой итерации. Я извлекаю элементы, которые вложены на разных уровнях.

XML-файл, который я анализирую, представляет собой файл TCX от Garmin. Краткий пример:

 <?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 

, поскольку HeartRateBpm отсутствует в третьем элементе Trackpoint

1 Ответ

0 голосов
/ 15 ноября 2018

Как я понимаю, вам нужно перебрать <Trackpoint> в <Track>.
Я предлагаю вам сделать это так:

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 (с удаленным <HeartRateBpm> в секунду <Trackpoint>) - 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'}]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...