2012-11-16 16 views
68

Ich möchte die Methode von "findall" verwenden, um einige Elemente der Quell-XML-Datei zu finden das Element Tree-Modul.Python ElementTree-Modul: Wie ignoriert man den Namespace von XML-Dateien, um übereinstimmendes Element zu finden, wenn die Methode "find", "findall"

Die Quell-XML-Datei (test.xml) verfügt jedoch über einen Namespace. Ich gestutzt Teil der XML-Datei als Beispiel:

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

Die Probe Python-Code ist unten:

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> 

Obwohl es kann funktioniert, weil es einen Namespace „{http: //www.test. com} ", ist es sehr unpraktisch, vor jedem Tag einen Namensraum hinzuzufügen.

Wie kann ich den Namespace ignorieren, wenn ich die Methode "find", "findall" usw. verwende?

+12

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

+0

Vielen Dank. Ich versuche deine Methode und es kann funktionieren. Es ist bequemer als meins, aber es ist immer noch ein bisschen peinlich.Wissen Sie, ob es im ElementTree-Modul keine andere geeignete Methode gibt, um dieses Problem zu lösen, oder dass es überhaupt keine solche Methode gibt? – KevinLeng

Antwort

33

Wenn Sie das xmlns-Attribut vor der Analyse aus dem XML entfernen, wird kein Namespace jedem Tag in der Struktur vorangestellt.

import re 

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

+100, jemand mint dieser Entwickler ein cryptocoin –

+2

Nur FYI das funktioniert nur auf Python 2.x Python 3.x wird werfen: TypeError: kann kein String-Muster auf einem Byte-ähnlichen Objekt verwenden –

+3

Dies funktionierte in vielen Fälle für mich, aber dann stieß ich auf mehrere Namespaces und Namespace-Aliase. Siehe meine Antwort für einen anderen Ansatz, der diese Fälle behandelt. – nonagon

3

Sie können die elegante String Formatierung konstruieren als auch verwenden:

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

oder, wenn Sie sicher sind, dass PAID_OFF nur in einer Ebene im Baum angezeigt:

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

Die Antworten haben den Namespace-Wert bisher explizit in das Skript eingefügt. Für eine allgemeinere Lösung würde extrahiere ich eher den Namespace aus dem xml:

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

Und es find-Methode verwenden:

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

Zu viel davon anzunehmen, dass es nur einen 'Namespace' gibt – Kashyap

38

Anstatt das XML-Dokument zu modifizieren selbst, dann ist es am besten, es zu analysieren und dann ändern Sie die Tags im Ergebnis. Auf diese Weise können mehrere Namespaces und Namespace-Aliase verarbeiten kann:

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 

Dies basiert auf der Diskussion hier: http://bugs.python.org/issue18304

+1

Dies. Das das das. Mehrere Namensräume würden der Tod von mir sein. – sheeptest

+4

OK, das ist nett und fortgeschritten, aber es ist nicht "et.findall ('{*} somethag')'. Und es ist auch Mangeln der Elementbaum selbst, nicht nur "führen Sie die Suche Ignorieren Namespaces nur dieses Mal, ohne das Dokument erneut zu analysieren usw. unter Beibehaltung der Namespace-Informationen". Nun, für diesen Fall müssen Sie wahrscheinlich durch den Baum iterieren und sehen, ob der Knoten Ihren Wünschen entspricht, nachdem Sie den Namespace entfernt haben. –

+0

Dies funktioniert, indem die Zeichenfolge entfernt wird, aber wenn ich die XML-Datei mit write (...) speichere, verschwindet der Namespace von dem Vorladen des XML xmlns = "http: // bla" verschwindet. Bitte um Rat – TraceKira

8

Hier ist eine Erweiterung nonagon Antwort, die auch Streifen Namespaces aus Attribute:

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

Wenn Sie ElementTree und nicht cElementTree verwenden, können Sie erzwingen, dass Expat die Namespace-Verarbeitung durch Ersetzen von ParserCreate():

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

ElementTree versucht Expat zu verwenden, indem ParserCreate() Aufruf aber bietet keine Option keinen Namespaceseparator Zeichenfolge zur Verfügung zu stellen, führt dazu, dass der obige Code es zu ignorieren, aber gewarnt werden, um andere Dinge brechen könnte.