使用Python/lxml和XPath检索属性名称和值
问题描述:
我正在使用XPath和Python lxml(Python 2)。我在数据上运行了两遍,一次选择感兴趣的记录,一次从数据中提取值。这是一个代码类型的示例。使用Python/lxml和XPath检索属性名称和值
from lxml import etree
xml = """
<records>
<row id="1" height="160" weight="80" />
<row id="2" weight="70" />
<row id="3" height="140" />
</records>
"""
parsed = etree.fromstring(xml)
nodes = parsed.xpath('/records/row')
for node in nodes:
print node.xpath("@id|@height|@weight")
当我运行此脚本输出:
['1', '160', '80']
['2', '70']
['3', '140']
正如你的结果,其中一个属性丢失看到,其他的位置属性的变化,所以我不能告诉在第2排和第3排是否是身高或体重。
有没有办法获得从etree/lxml返回的属性的名称?理想情况下,我应该看的格式结果:
[('@id', '1'), ('@height', '160'), ('@weight', '80')]
我承认,我可以使用ElementTree的和Python解决这个特定的情况下。但是,我希望使用XPath(以及相对简单的XPath)解决此问题,而不是使用python处理数据。
答
我断言我不打算使用Python是错误的。我发现lxml/etree实现很容易扩展到我可以使用XPath DSL进行修改。
我注册了函数“dictify”。我改变了XPath表达式:
dictify('@id|@height|@weight|weight|height')
新的代码是:
from lxml import etree
xml = """
<records>
<row id="1" height="160" weight="80" />
<row id="2" weight="70" ><height>150</height></row>
<row id="3" height="140" />
</records>
"""
def dictify(context, names):
node = context.context_node
rv = []
rv.append('__dictify_start_marker__')
names = names.split('|')
for n in names:
if n.startswith('@'):
val = node.attrib.get(n[1:])
if val != None:
rv.append(n)
rv.append(val)
else:
children = node.findall(n)
for child_node in children:
rv.append(n)
rv.append(child_node.text)
rv.append('__dictify_end_marker__')
return rv
etree_functions = etree.FunctionNamespace(None)
etree_functions['dictify'] = dictify
parsed = etree.fromstring(xml)
nodes = parsed.xpath('/records/row')
for node in nodes:
print node.xpath("dictify('@id|@height|@weight|weight|height')")
这将产生以下的输出:
['__dictify_start_marker__', '@id', '1', '@height', '160', '@weight', '80', '__dictify_end_marker__']
['__dictify_start_marker__', '@id', '2', '@weight', '70', 'height', '150', '__dictify_end_marker__']
['__dictify_start_marker__', '@id', '3', '@height', '140', '__dictify_end_marker__']
答
你应该尝试以下操作:
for node in nodes:
print node.attrib
这将返回节点的所有属性的字典为{'id': '1', 'weight': '80', 'height': '160'}
如果你想要得到的东西像[('@id', '1'), ('@height', '160'), ('@weight', '80')]
:
list_of_attributes = []
for node in nodes:
attrs = []
for att in node.attrib:
attrs.append(("@" + att, node.attrib[att]))
list_of_attributes.append(attrs)
输出:
[[('@id', '1'), ('@height', '160'), ('@weight', '80')], [('@id', '2'), ('@weight', '70')], [('@id', '3'), ('@height', '140')]]
是的,这样的作品,但它是Python的。我想使用XPath来提取数据。使用XPath允许我让用户定义访问路径。要在Python中实现,我将不得不编写某种形式的XPath DSL,这是毫无意义的,因为XPath是这个空间中的DSL。 –
这样做的技巧'/ records/row/@ */concat(name(),“,”,。)'? – Andersson
不幸的不是。这给出了一个错误。打印parsed.xpath('/ records/row/@ */concat(name(),“,”。)') lxml.etree.XPathEvalError:Invalid expression –