199 votes

Analyse syntaxique de XML avec espace de noms en Python via 'ElementTree'.

J'ai le XML suivant que je veux analyser à l'aide de la méthode Python. ElementTree :

<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>

Je veux trouver tous owl:Class puis extraire la valeur de toutes les balises rdfs:label des instances en leur sein. J'utilise le code suivant :

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

À cause de l'espace de noms, je reçois l'erreur suivante.

SyntaxError: prefix 'owl' not found in prefix map

J'ai essayé de lire le document à http://effbot.org/zone/element-namespaces.htm mais je n'arrive toujours pas à le faire fonctionner car le XML ci-dessus comporte plusieurs espaces de noms imbriqués.

Veuillez m'indiquer comment modifier le code pour trouver tous les éléments suivants owl:Class tags.

3voto

koffie Points 460

Il s'agit essentiellement de la réponse de Davide Brunato, mais j'ai découvert que sa réponse avait de sérieux problèmes : l'espace de noms par défaut est une chaîne vide, du moins sur mon installation python 3.6. La fonction que j'ai distillée à partir de son code et qui a fonctionné pour moi est la suivante :

from io import StringIO
from xml.etree import ElementTree
def get_namespaces(xml_string):
    namespaces = dict([
            node for _, node in ElementTree.iterparse(
                StringIO(xml_string), events=['start-ns']
            )
    ])
    namespaces["ns0"] = namespaces[""]
    return namespaces

ns0 est juste un substitut pour l'espace de nom vide et vous pouvez le remplacer par n'importe quelle chaîne aléatoire que vous voulez.

Si je le fais alors :

my_namespaces = get_namespaces(my_schema)
root.findall('ns0:SomeTagWithDefaultNamespace', my_namespaces)

Il produit également la réponse correcte pour les balises utilisant l'espace de nom par défaut.

1voto

peter.slizik Points 520

Ma solution est basée sur le commentaire de @Martijn Pieters :

register_namespace n'influence que la sérialisation, pas la recherche.

L'astuce consiste donc à utiliser des dictionnaires différents pour la sérialisation et pour la recherche.

namespaces = {
    '': 'http://www.example.com/default-schema',
    'spec': 'http://www.example.com/specialized-schema',
}

Maintenant, enregistrez tous les espaces de noms pour l'analyse et l'écriture :

for name, value in namespaces.iteritems():
    ET.register_namespace(name, value)

Pour la recherche ( find() , findall() , iterfind() ) nous avons besoin d'un préfixe non vide. Passez à ces fonctions un dictionnaire modifié (ici je modifie le dictionnaire original, mais cela ne doit être fait qu'après l'enregistrement des espaces de noms).

self.namespaces['default'] = self.namespaces['']

Maintenant, les fonctions de la find() peut être utilisé avec la famille default préfixe :

print root.find('default:myelem', namespaces)

mais

tree.write(destination)

n'utilise pas de préfixe pour les éléments de l'espace de noms par défaut.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X