152 votes

Module Python ElementTree: comment ignorer l'espace de noms des fichiers XML pour localiser l'élément correspondant lors de l'utilisation de la méthode "find", "findall"

Je veux utiliser la méthode de "findall" pour localiser certains éléments de la source de fichier xml dans le ElementTree module.

Cependant, la source xml (fichier test.xml) a espace de noms. Je tronquer une partie du fichier xml de l'échantillon:

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

L'exemple de code python est ci-dessous:

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>

Bien qu'il fonctionne, parce qu'il y est un espace de noms "{http://www.test.com}", c'est très gênant pour ajouter un espace de noms en face de chaque balise.

Comment puis-je ignorer l'espace de noms lors de l'utilisation de la méthode de "trouver", "findall" et ainsi de suite?

68voto

nonagon Points 434

Au lieu de modifier le document XML lui-même, il est préférable de l'analyser, puis de modifier les balises dans le résultat. De cette façon, vous pouvez gérer plusieurs espaces de noms et alias d'espaces de noms:

 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
 

Ceci est basé sur la discussion ici: http://bugs.python.org/issue18304

47voto

user2212280 Points 121

Si vous supprimez l'attribut xmlns du fichier xml avant de l'analyser, il n'y aura pas d'espace de nom ajouté à chaque balise dans l'arborescence.

 import re

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

19voto

wimous Points 59

Jusqu'à présent, les réponses inséraient explicitement la valeur de l'espace de noms dans le script. Pour une solution plus générique, je préférerais extraire l'espace de noms du xml:

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

Et utilisez-le dans la méthode find:

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

6voto

tzp Points 143

Vous pouvez également utiliser la structure élégante de formatage de chaîne:

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

ou, si vous êtes sûr que PAID_OFF apparaît uniquement dans un niveau de l'arborescence:

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

0voto

whatnick Points 3339

Vous pouvez remplacer des espaces de noms arbitraires dans la recherche par un caractère générique {*}. Donc, trouver des éléments par balise devient:

 pro_doc.findall('//{*}Element)
 

Cela trouvera tous les éléments appelés élément indépendamment de l'espace de noms.

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