Análisis de XML con espacio de nombres en Python a través de ‘ElementTree’

Tengo el siguiente XML que deseo analizar utilizando el ElementTree de Python:

   basketball league  a group of sports teams that compete against each other in Basketball    

Quiero encontrar todas owl:Class tags owl:Class y luego extraer el valor de todas las instancias rdfs:label dentro de ellas. Estoy usando el siguiente código:

 tree = ET.parse("filename") root = tree.getroot() root.findall('owl:Class') 

Debido al espacio de nombres, recibo el siguiente error.

 SyntaxError: prefix 'owl' not found in prefix map 

Intenté leer el documento en http://effbot.org/zone/element-namespaces.htm pero todavía no puedo hacer que funcione, ya que el XML anterior tiene múltiples espacios de nombres nesteds.

Por favor, dime cómo cambiar el código para encontrar todas las tags owl:Class .

ElementTree no es demasiado inteligente sobre los espacios de nombres. .find() proporcionar a los .find() , findall() e iterfind() un diccionario de espacio de nombres explícito. Esto no está muy bien documentado:

 namespaces = {'owl': 'http://www.w3.org/2002/07/owl#'} # add more as needed root.findall('owl:Class', namespaces) 

Los prefijos solo se buscan en el parámetro de namespaces que pasa. Esto significa que puede usar cualquier prefijo de espacio de nombres que desee; la API se separa del owl: parte, busca la URL del namespaces nombres correspondiente en el diccionario de namespaces , luego cambia la búsqueda para buscar la expresión XPath {http://www.w3.org/2002/07/owl}Class . Por supuesto, también puede utilizar la misma syntax:

 root.findall('{http://www.w3.org/2002/07/owl#}Class') 

Si puedes cambiar a la biblioteca lxml , las cosas son mejores; esa biblioteca admite la misma API de ElementTree, pero recostack espacios de nombres para usted en un atributo .nsmap en los elementos.

Aquí se explica cómo hacer esto con lxml sin tener que codificar los espacios de nombres ni escanear el texto en busca de ellos (como menciona Martijn Pieters):

 from lxml import etree tree = etree.parse("filename") root = tree.getroot() root.findall('owl:Class', root.nsmap) 

Nota : Esta es una respuesta útil para la biblioteca estándar de Python’s ElementTree sin usar espacios de nombres codificados.

Para extraer los prefijos del espacio de nombres y el URI de los datos XML, puede usar la función ElementTree.iterparse , analizando solo los eventos de inicio de espacio de nombres ( start-ns ):

 >>> from io import StringIO >>> from xml.etree import ElementTree >>> my_schema = u''' ... ...  ... basketball league ...  ... a group of sports teams that compete against each other ... in Basketball ...  ...  ... ... ''' >>> my_namespaces = dict([ ... node for _, node in ElementTree.iterparse( ... StringIO(my_schema), events=['start-ns'] ... ) ... ]) >>> from pprint import pprint >>> pprint(my_namespaces) {'': 'http://dbpedia.org/ontology/', 'owl': 'http://www.w3.org/2002/07/owl#', 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', 'xsd': 'http://www.w3.org/2001/XMLSchema#'} 

Entonces el diccionario se puede pasar como argumento a las funciones de búsqueda:

 root.findall('owl:Class', my_namespaces) 

He estado usando un código similar a este y he encontrado que siempre vale la pena leer la documentación … ¡como siempre!

findall () solo encontrará elementos que sean hijos directos de la etiqueta actual . Por lo tanto, no realmente todo.

Puede que valga la pena intentar que su código funcione con lo siguiente, especialmente si está tratando con archivos xml grandes y complejos para que también se incluyan los sub-sub-elementos (etc.). Si sabes dónde están los elementos en tu xml, entonces supongo que estará bien. Solo pensé que esto valía la pena recordar.

 root.iter() 

ref: https://docs.python.org/3/library/xml.etree.elementtree.html#finding-interesting-elements “Element.findall () encuentra solo elementos con una etiqueta que son hijos directos del elemento actual. Element.find () encuentra el primer elemento secundario con una etiqueta en particular y Element.text accede al contenido del texto del elemento. Element.get () accede a los atributos del elemento: ”

Para obtener el espacio de nombres en su formato de espacio de nombres, por ejemplo, {myNameSpace} , puede hacer lo siguiente:

 root = tree.getroot() ns = re.match(r'{.*}', root.tag).group(0) 

De esta manera, puede usarlo más adelante en su código para encontrar nodos, por ejemplo, mediante la interpolación de cadenas (Python 3).

 link = root.find(f'{ns}link') 

Sé que tengo algunos años de retraso, pero acabo de crear un paquete que manejará la conversión de un diccionario a XML válido con espacios de nombres. El paquete está alojado en PyPi @ https://pypi.python.org/pypi/xmler .

Usando este paquete puedes tomar un diccionario que se ve así:

 myDict = { "RootTag": { # The root tag. Will not necessarily be root. (see #customRoot) "@ns": "soapenv", # The namespace for the RootTag. The RootTag will appear as  "@attrs": { # @attrs takes a dictionary. each key-value pair will become an attribute { "xmlns:soapenv": "http://schemas.xmlsoap.org/soap/envelope/" } }, "childTag": { "@attrs": { "someAttribute": "colors are nice" }, "grandchild": "This is a text tag" } } } 

y obtener una salida XML que se ve así:

   This is a text tag   

Espero que esto sea útil para las personas en el futuro.