2012-11-16 12 views
68

Voglio utilizzare il metodo di "findall" per individuare alcuni elementi del file xml di origine in il modulo ElementTree.Modulo Python ElementTree: come ignorare lo spazio dei nomi dei file XML per individuare l'elemento corrispondente quando si utilizza il metodo "trova", "findall"

Tuttavia, il file xml di origine (test.xml) ha spazio dei nomi. I Tronca parte del file XML come campione:

<?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'esempio di codice Python è qui sotto:

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> 

Anche se può funzionare, perché non v'è uno spazio dei nomi "{http: //www.test. com} ", è molto sconveniente aggiungere uno spazio dei nomi davanti a ogni tag.

Come posso ignorare lo spazio dei nomi quando si utilizza il metodo di "trova", "findall" e così via?

+12

Is 'tree.findall (" xmlns: DEAL_LEVEL/xmlns: PAID_OFF ", namespaces = {'xmlns': 'http://www.test.com'})' abbastanza conveniente? – iMom0

+0

Grazie mille. Provo il tuo metodo e può funzionare. È più comodo del mio ma è ancora un po 'imbarazzante.Sai se non esiste un altro metodo corretto nel modulo ElementTree per risolvere questo problema o non esiste un metodo del genere? – KevinLeng

risposta

33

Se si rimuove l'attributo xmlns da xml prima di analizzarlo, non ci sarà uno spazio dei nomi inserito in ogni tag nell'albero.

import re 

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

+100, qualcuno con questo sviluppatore una criptocostituzione –

+2

Solo FYI funziona solo su python 2.x python 3.x getterà: TypeError: impossibile usare un modello di stringa su un oggetto simile a un byte –

+3

Questo ha funzionato in molti casi per me, ma poi mi sono imbattuto in più spazi dei nomi e alias namespace. Vedi la mia risposta per un altro approccio che gestisce questi casi. – nonagon

3

È possibile utilizzare l'elegante formattazione delle stringhe costruire così:

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

o, se sei sicuro che PAID_OFF appare solo in un livello in albero:

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

Le risposte finora hanno esplicitamente inserito il valore del namespace nello script. Per una soluzione più generica, avrei preferito estrarre lo spazio dei nomi dal xml:

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

e utilizzarlo in metodo Find:

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

Troppo da assumere che esiste un solo 'namespace' – Kashyap

38

Invece di modificare il documento XML in sé, è meglio analizzarlo e quindi modificare i tag nel risultato. In questo modo è possibile gestire più domini e gli alias di namespace:

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 

Questo si basa sulla discussione qui: http://bugs.python.org/issue18304

+1

Questo. Questo questo questo. Più spazi di nomi sarebbero stati la mia morte. – sheeptest

+4

OK, questo è bello e più avanzato, ma ancora non è 'et.findall ('{*} sometag')'. Ed inoltre sta manipolando l'albero degli elementi, non solo "esegue la ricerca ignorando gli spazi dei nomi solo questa volta, senza ri-analizzare il documento ecc, mantenendo le informazioni sullo spazio dei nomi". Bene, per quel caso è necessario scorrere iteramente attraverso l'albero e vedere di persona se il nodo corrisponde ai tuoi desideri dopo aver rimosso lo spazio dei nomi. –

+0

Questo funziona spogliando la stringa ma quando salvi il file XML usando write (...) lo spazio dei nomi scompare dal mendicare XML xmlns = "http: // bla" scompare. Per favore consiglio – TraceKira

8

Ecco una proroga per la risposta di nonagon, che mette a nudo anche i namespace fuori gli attributi:

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 
0

Se si utilizza ElementTree e non cElementTree, è possibile forzare Expat a ignorare l'elaborazione dello spazio dei nomi sostituendo ParserCreate():

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

ElementTree cercherà di utilizzare Expat chiamando ParserCreate() ma fornisce alcuna possibilità di non fornire una stringa separatore di namespace, il codice sopra farà sì che venga ignorare ma attenzione questo potrebbe rompere altre cose.