使用命名空间的Python XML XPath查询标签和属性
我觉得我可能做错了什么,每个我在StackOverflow上看到的例子似乎都表明这样做是可以的。
我正在尝试使用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循环里面处理这个,但我觉得应该可以在一行内完成!
3 个回答
1
解决这个问题的一种方法是避免指定属性名称,而是使用 *
:
.//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
希望这对你有帮助。
1
如果其他方法都不行,你仍然可以使用
".//xsi:SpeedZone[@*[name() = 'xsi:type' and . = 'PredefinedSpeedZone_t']]"
使用 name()
这个方法虽然没有直接使用带命名空间的属性那么方便,但至少 etree 能够理解这个方法。
4
我来得有点晚,但我觉得其他的回答有点让人困惑。
在问题中的Python代码以及另外两个回答中,xsi
这个前缀是和http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2
这个网址绑定的。但是在包含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>