python lxml findall 具有多个命名空间
Posted
技术标签:
【中文标题】python lxml findall 具有多个命名空间【英文标题】:python lxml findall with multiple namespaces 【发布时间】:2016-08-15 02:47:50 【问题描述】:我正在尝试使用 lxml 解析具有多个命名空间的 XML 文档,但我一直坚持让 findall() 方法返回一些内容。
我的 XML:
<MeasurementRecords xmlns="http://www.company.com/common/rsp/2012/07"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.company.com/common/rsp/2012/07 RSP_EWS_V1.6.xsd">
<HistoryRecords>
<ValueItemId>100_0000100004_3788_Resource-0.customId_WSx Data Precip Type</ValueItemId>
<List>
<HistoryRecord>
<Value>60</Value>
<State>Valid</State>
<TimeStamp>2016-04-20T12:40:00Z</TimeStamp>
</HistoryRecord>
</List>
</HistoryRecords>
<HistoryRecords>
</MeasurementRecords>
我的代码:
from lxml import etree
from pprint import pprint
RSPxmlFile = '/home/user/Desktop/100_0000100004_3788_20160420144011263_records.xml'
with open (RSPxmlFile, 'rt') as f:
tree = etree.parse(f)
root = tree.getroot()
for node in tree.findall('MeasurementRecords', root.nsmap):
print node
print "parameter = ", node.text
给予:
ValueError: empty namespace prefix is not supported in ElementPath
阅读this后尝试的一些实验:
>>> root.nsmap
'xsi': 'http://www.w3.org/2001/XMLSchema-instance', None: http://www.company.com/common/rsp/2012/07'
>>> nsmap['foo']=nsmap[None]
>>> nsmap.pop(None)
'http://www.company.com/common/rsp/2012/07'
>>> nsmap
'xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'foo': 'http://www.company.com/common/rsp/2012/07'
>>> tree.xpath("//MeasurementRecords", namespaces=nsmap)
[]
>>> tree.xpath('/foo:MeasurementRecords', namespaces=nsmap)
[<Element http://www.company.com/common/rsp/2012/07MeasurementRecords at 0x6ffffda5290>]
>>> tree.xpath('/foo:MeasurementRecords/HistoryRecords', namespaces=nsmap)
[]
但这似乎没有帮助。
所以,更多的实验:
>>> tree.findall('//http://www.company.com/common/rsp/2012/07MeasurementRecords')
[]
>>> print root
<Element http://www.company.com/common/rsp/2012/07MeasurementRecords at 0x6ffffda5290>
>>> print tree
<lxml.etree._ElementTree object at 0x6ffffda5368>
>>> for node in tree.iter():
... print node
...
<Element http://www.company.com/common/rsp/2012/07MeasurementRecords at 0x6ffffda5290>
<Element http://www.company.com/common/rsp/2012/07HistoryRecords at 0x6ffffda5cf8>
<Element http://www.company.com/common/rsp/2012/07ValueItemId at 0x6ffffda5f38>
...etc...
>>> tree.findall("//HistoryRecords", namespaces=nsmap)
[]
>>> tree.findall("//foo:MeasurementRecords/HistoryRecords", namespaces=nsmap)
[]
我被难住了。我不知道出了什么问题。
【问题讨论】:
【参考方案1】:如果你从这个开始:
>>> tree = etree.parse(open('data.xml'))
>>> root = tree.getroot()
>>>
这将无法找到任何元素...
>>> root.findall('http://www.company.com/common/rsp/2012/07MeasurementRecords')
[]
...但那是因为root
是 MeasurementRecords
元素;它
不包含任何MeasurementRecords
元素。在另一
手,以下工作就好了:
>>> root.findall('http://www.company.com/common/rsp/2012/07HistoryRecords')
[<Element http://www.company.com/common/rsp/2012/07HistoryRecords at 0x7fccd0332ef0>]
>>>
使用xpath
方法,您可以执行以下操作:
>>> nsmap='a': 'http://www.company.com/common/rsp/2012/07',
... 'b': 'http://www.w3.org/2001/XMLSchema-instance'
>>> root.xpath('//a:HistoryRecords', namespaces=nsmap)
[<Element http://www.company.com/common/rsp/2012/07HistoryRecords at 0x7fccd0332ef0>]
所以:
findall
和 find
方法需要 ...namespace...ElementName
语法。
xpath
方法需要命名空间前缀 (ns:ElementName
),它在提供的 namespaces
映射中查找。 prefix 不必匹配原始文档中使用的前缀,但 namespace url 必须匹配。
所以这行得通:
>>> root.find('http://www.company.com/common/rsp/2012/07HistoryRecords/http://www.company.com/common/rsp/2012/07ValueItemId')
<Element http://www.company.com/common/rsp/2012/07ValueItemId at 0x7fccd0332a70>
或者这行得通:
>>> root.xpath('/a:MeasurementRecords/a:HistoryRecords/a:ValueItemId',namespaces=nsmap)
[<Element http://www.company.com/common/rsp/2012/07ValueItemId at 0x7fccd0330830>]
【讨论】:
太棒了!非常感谢。是否可以从一个ValueItemId
元素中返回 Value
节点?我试过这个,但没有用:root.xpath('/a:MeasurementRecords/a:HistoryRecords[a:ValueItemId="100_0000100004_3788_Resource-0.customId_WSx Data Precip Type"]//a:Value',namespaces=nsmap)
This question 可能会有所帮助。以上是关于python lxml findall 具有多个命名空间的主要内容,如果未能解决你的问题,请参考以下文章
Python3.X BeautifulSoup([your markup], "lxml") markup_type=markup_type))的解决方案