Анализ XML с помощью Python – элементов доступа

Я использую lxml для анализа некоторого xml, но по какой-то причине я не могу найти определенный элемент.

Я пытаюсь получить доступ к элементам <Constant> .

Вот фрагмент xml:

  </rdf:Description> </rdf:RDF> </MiriamAnnotation> <ListOfSubstrates> <Substrate metabolite="Metabolite_5" stoichiometry="1"/> </ListOfSubstrates> <ListOfModifiers> <Modifier metabolite="Metabolite_9" stoichiometry="1"/> </ListOfModifiers> <ListOfConstants> <Constant key="Parameter_4344" name="Kcat" value="433.724"/> <Constant key="Parameter_4343" name="km" value="479.617"/> 

Код, который я использую, выглядит следующим образом:

  >>> from lxml import etree as ET >>> parsed = ET.parse('ct.cps') >>> root = parsed.getroot() >>> for a in root.findall(".//Constant"): ... print a.attrib['key'] ... >>> for a in root.findall('Constant'): ... print a.get('key') ... >>> for a in root.findall('Constant'): ... print a.attrib['key'] ... 

Как вы можете видеть, ни одна из этих вещей не работает.

Что я делаю не так?


EDIT: Мне интересно, связано ли это с тем, что элементы <Constant> пустые?


EDIT2: Исходный xml здесь: https://www.dropbox.com/s/i6hga7nvmcd6rxx/ct.cps?dl=0

Вот как вы можете получить значения, которые вы ищете:

 from lxml import etree parsed = etree.parse('ct.cps') for a in parsed.findall("//{http://www.copasi.org/static/schema}Constant"): print a.attrib["key"] 

Вывод:

 Parameter_4344 Parameter_4343 Parameter_4342 Parameter_4341 Parameter_4340 Parameter_4339 Parameter_4338 Parameter_4337 Parameter_4336 Parameter_4335 Parameter_4334 Parameter_4333 Parameter_4332 Parameter_4331 Parameter_4330 Parameter_4329 Parameter_4328 Parameter_4327 Parameter_4326 Parameter_4325 Parameter_4324 Parameter_4323 Parameter_4322 Parameter_4321 Parameter_4320 Parameter_4319 

Важно то, что корневой элемент COPASI в вашем XML-файле (реальный на URL-адрес Dropbox) объявляет пространство имен по умолчанию ( http://www.copasi.org/static/schema ). Это означает, что элемент и все его потомки, включая Constant , принадлежат этому пространству имен.

Поэтому вместо элементов Constant вам нужно искать элементы {http://www.copasi.org/static/schema}Constant .

См. http://lxml.de/tutorial.html#namespaces .


Вот как вы могли это сделать, используя XPath вместо findall :

 from lxml import etree NSMAP = {"c": "http://www.copasi.org/static/schema"} parsed = etree.parse('ct.cps') for a in parsed.xpath("//c:Constant", namespaces=NSMAP): print a.attrib["key"] 

См. http://lxml.de/xpathxslt.html#namespaces-and-prefixes .

Во-первых, пожалуйста, не обращайте внимания на мои комментарии. Оказывается, что xml.etree намного лучше стандартного xml.etree.ElementTree в том, что он заботится о пространстве имен. Проблема заключается в том, что вы хотите найти '//Constant' , что означает, что узлы могут быть на любом уровне. Однако корневой элемент не позволяет это сделать:

 >>> root.findall('//Constant') SyntaxError: cannot use absolute path on element 

Однако вы можете сделать это на более высоком уровне:

 >>> parsed.findall('//Constant') [<Element Constant at 0x10a7ce128>, <Element Constant at 0x10a7ce170>] 

Обновить

Я размещаю здесь полный текст. Поскольку у меня нет полного файла XML, я делаю что-то, чтобы заполнить пробел.

 from lxml import etree as ET from StringIO import StringIO xml_text = """<?xml version='1.0' encoding='utf-8' ?> <rdf:root xmlns:rdf='http://foo.bar.com/rdf'> <rdf:RDF> <rdf:Description> DescriptionX </rdf:Description> </rdf:RDF> <rdf:foo> <MiriamAnnotation> bar </MiriamAnnotation> <ListOfSubstrates> <Substrate metabolite="Metabolite_5" stoichiometry="1"/> </ListOfSubstrates> <ListOfModifiers> <Modifier metabolite="Metabolite_9" stoichiometry="1"/> </ListOfModifiers> <ListOfConstants> <Constant key="Parameter_4344" name="Kcat" value="433.724"/> <Constant key="Parameter_4343" name="km" value="479.617"/> </ListOfConstants> </rdf:foo> </rdf:root> """ buffer = StringIO(xml_text) tree = ET.parse(buffer) for constant_node in tree.findall('//Constant'): print constant_node.attrib['key'] 

Не используйте findall . Он имеет ограниченный набор функций и предназначен для совместимости с ElementTree .

Вместо этого используйте xpath , который поддерживает пространства имен. Из вышесказанного кажется, что вы, вероятно, хотите сказать что-то вроде

 # possibilities, you need to get these right... ns_dict = {'atom':"http://www.w3.org/2005/Atom",, "rdf":"http://www.w3.org/2000/01/rdf-schema#" } root = parsed.getroot() for a in root.xpath('.//rdf:Constant', namespaces=ns_dict): print a.attrib['key'] 

Обратите внимание, что вы должны включать префикс пространства имен в ваше выражение xpath всякий раз, когда элемент имеет непустое пространство имен, и они должны сопоставляться с одним из URL-адресов пространства имен, которые соответствуют тем же URL-адресам в вашем документе.

Обновить

Поскольку вы разместили свой исходный документ, я вижу, что нет пространства имен, назначенного для элементов, которые вы ищете. Это будет работать, я просто попробовал его с исходным документом:

 for a in tree.xpath("//Constant"): print a.attrib['key'] 

Вам не требуется пространство имен, поскольку в самом документе не указано пространство имен по умолчанию.