open - Análisis de XML con espacio de nombre en Python a través de ''ElementTree''
xml etree python 3 (5)
Aquí se explica cómo hacer esto con lxml sin tener que codificar los espacios de nombres o escanear el texto para ellos (como menciona Martijn Pieters):
from lxml import etree
tree = etree.parse("filename")
root = tree.getroot()
root.findall(''owl:Class'', root.nsmap)
Tengo el siguiente XML que quiero analizar con ElementTree
de Python:
<rdf:RDF xml:base="http://dbpedia.org/ontology/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns="http://dbpedia.org/ontology/">
<owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
<rdfs:label xml:lang="en">basketball league</rdfs:label>
<rdfs:comment xml:lang="en">
a group of sports teams that compete against each other
in Basketball
</rdfs:comment>
</owl:Class>
</rdf:RDF>
Quiero buscar todas owl:Class
etiquetas owl:Class
y luego extraer el valor de todas las instancias de 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 aún no puedo hacerlo funcionar, ya que el XML anterior tiene múltiples espacios de nombres anidados.
Por favor, hágamelo saber cómo cambiar el código para encontrar todos los owl:Class
etiquetas de owl:Class
.
ElementTree no es demasiado inteligente con los espacios de nombres. .find()
dar a los .find()
, findall()
e iterfind()
un diccionario de espacio de nombres explícito. Esto no está documentado muy bien:
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 se transfieren. Esto significa que puede usar cualquier prefijo de espacio de nombres que desee; la API divide el 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
lugar. También puede usar la misma sintaxis, por supuesto:
root.findall(''{http://www.w3.org/2002/07/owl#}Class'')
Si puede cambiar a la biblioteca lxml
, las cosas son mejores; esa biblioteca admite la misma API ElementTree, pero recopila espacios de nombres para usted en un atributo .nsmap
en elementos.
He estado usando un código similar a este y he descubierto que siempre vale la pena leer la documentación ... ¡como de costumbre!
findall () solo encontrará elementos que son hijos directos de la etiqueta actual . Entonces, no realmente TODOS.
Podría valer la pena intentar que tu código funcione con lo siguiente, especialmente si se trata de archivos xml grandes y complejos para que también se incluyan los sub-subelementos (etc.). Si sabes dónde están los elementos en tu xml, entonces supongo que estará bien. Solo pensé que esto era digno de recordar.
root.iter()
ref: https://docs.python.org/3/library/xml.etree.elementtree.html#finding-interesting-elements "Element.findall () solo encuentra 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 de texto del elemento. Element.get () accede a los atributos del elemento: "
Sé que llegué unos años tarde, 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 puede 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 <soapenv:RootTag ...>
"@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 obtenga una salida XML que se ve así:
<soapenv:RootTag xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<childTag someAttribute="colors are nice">
<grandchild>This is a text tag</grandchild>
</childTag>
</soapenv:RootTag>
Espero que esto sea útil para las personas en el futuro
Nota : Esta es una respuesta útil para la biblioteca de ElementTree de Python sin utilizar espacios de nombres codificados.
Para extraer los prefijos y el URI del espacio de nombres de los datos XML, puede usar la función ElementTree.iterparse
, analizando solo los eventos de inicio del espacio de nombres ( start-ns ):
>>> from io import StringIO
>>> from xml.etree import ElementTree
>>> my_schema = u''''''<rdf:RDF xml:base="http://dbpedia.org/ontology/"
... xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
... xmlns:owl="http://www.w3.org/2002/07/owl#"
... xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
... xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
... xmlns="http://dbpedia.org/ontology/">
...
... <owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
... <rdfs:label xml:lang="en">basketball league</rdfs:label>
... <rdfs:comment xml:lang="en">
... a group of sports teams that compete against each other
... in Basketball
... </rdfs:comment>
... </owl:Class>
...
... </rdf:RDF>''''''
>>> 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)