2017-02-23 2 views
0

Я использую 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. Тем не менее, я хочу разрешить это с помощью XPaths (и относительно простых XPaths), а не обрабатывать данные с помощью python.

ответ

0

Я был неправ в своем утверждении, что я не собираюсь использовать Python. Я обнаружил, что реализация lxml/etree легко распространяется на то, что я могу использовать DSath XPath с изменениями.

Я зарегистрировал функцию «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__'] 
1

Вы должны попробовать следующее:

for node in nodes: 
    print node.attrib 

Это вернет Dict всех атрибутов узла, как {'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')]] 
+0

Да, это работает, но это Python. Я хочу использовать XPath для извлечения данных. Использование XPath позволяет мне определять пути доступа. Для реализации на Python мне придется написать некоторую форму DSath DSath, которая бессмысленна, учитывая, что XPath является DSL в этом пространстве. –

+0

Делает ли это трюк '/ records/row/@ */concat (name(),", ",.)'? – Andersson

+0

К сожалению, нет. Это дает ошибку. print parsed.xpath ('/ records/row/@ */concat (name(), ",".)') lxml.etree.XPathEvalError: недопустимое выражение –

Смежные вопросы