Я анализирую 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