Obtener una lista de valores de atributos XML en Python
-
01-07-2019 - |
Pregunta
Necesito obtener una lista de valores de atributos de elementos secundarios en Python.
Es más fácil de explicar con un ejemplo.
Dado un XML como este:
<elements>
<parent name="CategoryA">
<child value="a1"/>
<child value="a2"/>
<child value="a3"/>
</parent>
<parent name="CategoryB">
<child value="b1"/>
<child value="b2"/>
<child value="b3"/>
</parent>
</elements>
Quiero poder hacer algo como:
>>> getValues("CategoryA")
['a1', 'a2', 'a3']
>>> getValues("CategoryB")
['b1', 'b2', 'b3']
Parece un trabajo para XPath pero estoy abierto a todas las recomendaciones.También me gustaría conocer sus bibliotecas XML de Python favoritas.
Solución
Realmente no soy un experto en Python, pero aquí hay una solución XPath que usa libxml2.
import libxml2
DOC = """<elements>
<parent name="CategoryA">
<child value="a1"/>
<child value="a2"/>
<child value="a3"/>
</parent>
<parent name="CategoryB">
<child value="b1"/>
<child value="b2"/>
<child value="b3"/>
</parent>
</elements>"""
doc = libxml2.parseDoc(DOC)
def getValues(cat):
return [attr.content for attr in doc.xpathEval("/elements/parent[@name='%s']/child/@value" % (cat))]
print getValues("CategoryA")
Con resultado...
['a1', 'a2', 'a3']
Otros consejos
Árbol de elementos 1.3 (desafortunadamente no 1.2, que es el que se incluye con Python) soporta XPath como esto:
import elementtree.ElementTree as xml
def getValues(tree, category):
parent = tree.find(".//parent[@name='%s']" % category)
return [child.get('value') for child in parent]
Entonces puedes hacer
>>> tree = xml.parse('data.xml')
>>> getValues(tree, 'CategoryA')
['a1', 'a2', 'a3']
>>> getValues(tree, 'CategoryB')
['b1', 'b2', 'b3']
lxml.etree
(que también proporciona la interfaz ElementTree) también funcionará de la misma manera.
Usando un DOM W3 estándar como el minidom de stdlib o pxdom:
def getValues(category):
for parent in document.getElementsByTagName('parent'):
if parent.getAttribute('name')==category:
return [
el.getAttribute('value')
for el in parent.getElementsByTagName('child')
]
raise ValueError('parent not found')
Debo admitir que soy fan de xmltramp debido a su facilidad de uso.
Accediendo a lo anterior se convierte en:
import xmltramp
values = xmltramp.parse('''...''')
def getValues( values, category ):
cat = [ parent for parent in values['parent':] if parent(name) == category ]
cat_values = [ child(value) for child in parent['child':] for parent in cat ]
return cat_values
getValues( values, "CategoryA" )
getValues( values, "CategoryB" )
Puedes hacer esto con hermosasopa
>>> from BeautifulSoup import BeautifulStoneSoup
>>> soup = BeautifulStoneSoup(xml)
>>> def getValues(name):
. . . return [child['value'] for child in soup.find('parent', attrs={'name': name}).findAll('child')]
Si está trabajando con HTML/XML, le recomendaría que eche un vistazo a BeautifulSoup.Es similar al árbol DOM pero contiene más funciones.
Mi biblioteca xml de Python preferida es lxml , que envuelve libxml2.
XPath parece el camino a seguir aquí, así que escribiría esto como algo como:
from lxml import etree
def getValues(xml, category):
return [x.attrib['value'] for x in
xml.findall('/parent[@name="%s"]/*' % category)]
xml = etree.parse(open('filename.xml'))
>>> print getValues(xml, 'CategoryA')
['a1', 'a2', 'a3']
>>> print getValues(xml, 'CategoryB')
['b1', 'b2', 'b3]
En Python 3.x, obtener una lista de atributos es una tarea sencilla que consiste en utilizar el miembro items()
Utilizando el ElementTree
, el siguiente fragmento muestra una forma de obtener la lista de atributos.TENGA EN CUENTA que este ejemplo no considera los espacios de nombres, que, si están presentes, deberán contabilizarse.
import xml.etree.ElementTree as ET
flName = 'test.xml'
tree = ET.parse(flName)
root = tree.getroot()
for element in root.findall('<child-node-of-root>'):
attrList = element.items()
print(len(attrList), " : [", attrList, "]" )
REFERENCIA:
Elemento.elementos()
Devuelve los atributos del elemento como una secuencia de pares (nombre, valor).
Los atributos se devuelven en un orden arbitrario.