from example etree python namespaces find elementtree findall

python - from - xml.etree.elementtree example



Módulo Python ElementTree: cómo ignorar el espacio de nombres de los archivos XML para localizar el elemento coincidente al usar el método "find", "findall" (6)

Aquí hay una extensión de la respuesta de nonagon, que también elimina los espacios de nombres de los atributos:

from StringIO import StringIO import xml.etree.ElementTree as ET # instead of ET.fromstring(xml) it = ET.iterparse(StringIO(xml)) for _, el in it: if ''}'' in el.tag: el.tag = el.tag.split(''}'', 1)[1] # strip all namespaces for at in el.attrib.keys(): # strip namespaces of attributes too if ''}'' in at: newat = at.split(''}'', 1)[1] el.attrib[newat] = el.attrib[at] del el.attrib[at] root = it.root

Quiero utilizar el método de "findall" para localizar algunos elementos del archivo xml fuente en el módulo ElementTree.

Sin embargo, el archivo xml fuente (test.xml) tiene espacio de nombres. Trunco ​​parte del archivo xml como muestra:

<?xml version="1.0" encoding="iso-8859-1"?> <XML_HEADER xmlns="http://www.test.com"> <TYPE>Updates</TYPE> <DATE>9/26/2012 10:30:34 AM</DATE> <COPYRIGHT_NOTICE>All Rights Reserved.</COPYRIGHT_NOTICE> <LICENSE>newlicense.htm</LICENSE> <DEAL_LEVEL> <PAID_OFF>N</PAID_OFF> </DEAL_LEVEL> </XML_HEADER>

El código python de muestra está a continuación:

from xml.etree import ElementTree as ET tree = ET.parse(r"test.xml") el1 = tree.findall("DEAL_LEVEL/PAID_OFF") # Return None el2 = tree.findall("{http://www.test.com}DEAL_LEVEL/{http://www.test.com}PAID_OFF") # Return <Element ''{http://www.test.com}DEAL_LEVEL/PAID_OFF'' at 0xb78b90>

Aunque puede funcionar, porque hay un espacio de nombres "{http://www.test.com}", es muy inconveniente agregar un espacio de nombres al frente de cada etiqueta.

¿Cómo puedo ignorar el espacio de nombres cuando uso el método de "buscar", "findall" y demás?


En lugar de modificar el documento XML en sí, es mejor analizarlo y luego modificar las etiquetas en el resultado. De esta forma puede manejar múltiples espacios de nombres y alias de espacio de nombres:

from StringIO import StringIO import xml.etree.ElementTree as ET # instead of ET.fromstring(xml) it = ET.iterparse(StringIO(xml)) for _, el in it: if ''}'' in el.tag: el.tag = el.tag.split(''}'', 1)[1] # strip all namespaces root = it.root

Esto se basa en la discusión aquí: http://bugs.python.org/issue18304


Las respuestas hasta ahora explícitamente ponen el valor del espacio de nombres en el script. Para una solución más genérica, preferiría extraer el espacio de nombres del xml:

import re def get_namespace(element): m = re.match(''/{.*/}'', element.tag) return m.group(0) if m else ''''

Y úsala en el método de búsqueda:

namespace = get_namespace(tree.getroot()) print tree.find(''./{0}parent/{0}version''.format(namespace)).text


Si está utilizando ElementTree y no cElementTree , puede forzar a Expat a ignorar el procesamiento del espacio de nombres reemplazando a ParserCreate() :

from xml.parsers import expat oldcreate = expat.ParserCreate expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

ElementTree intenta utilizar Expat llamando a ParserCreate() pero no proporciona ninguna opción para no proporcionar una cadena de separador de espacio de nombre, el código anterior hará que se ignore, pero tenga en cuenta que esto podría romper otras cosas.


Si quita el atributo xmlns del xml antes de analizarlo, no habrá un espacio de nombre antes de cada etiqueta en el árbol.

import re xmlstring = re.sub('' xmlns="[^"]+"'', '''', xmlstring, count=1)


También puede usar la elegante construcción de formato de cadena:

ns=''http://www.test.com'' el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))

o, si está seguro de que PAID_OFF solo aparece en un nivel en el árbol:

el2 = tree.findall(".//{%s}PAID_OFF" % ns)