2016-11-15 5 views
1

Ich habe ein Python-Skript verwendet, um XML zu analysieren und bestimmte Elemente von Interesse in eine CSV-Datei zu exportieren. Ich habe versucht jetzt, um das Skript zu ändern, um die Filterung einer XML-Datei unter einem Kriterium, das äquivalent XPath-Abfrage wäre zu ermöglichen:Verwendung von XPath in Python mit LXML

\DC\Events\Confirmation[contains(TransactionId,"GTEREVIEW")] 

Wenn ich lxml zu verwenden versuchen, dies zu tun, ist mein Code:

xml_file = lxml.etree.parse(xml_file_path) 
namespace = "{" + xml_file.getroot().nsmap[None] + "}" 
node_list = xml_file.findall(namespace + "Events/" + namespace + "Confirmation[TransactionId='*GTEREVIEW*']") 

Aber das scheint nicht zu funktionieren. Kann jemand helfen? Beispiel für XML-Datei:

<Events> 
    <Confirmation> 
    <TransactionId>GTEREVIEW2012</TransactionId> 
    </Confirmation>  
    <Confirmation> 
    <TransactionId>GTEDEF2012</TransactionId> 
    </Confirmation>  
</Events> 

Deshalb möchte ich alle „Bestätigung“ Knoten, die eine Transaktions-ID enthalten, die die Zeichenfolge „GTEREVIEW“ enthält. Dank

+0

Wo ist Ihre XML-Datei? – SomeDude

+0

Ich habe die Frage aktualisiert. – naiminp

Antwort

2

findall() nicht XPath-Ausdrücke unterstützt, nur ElementPath (siehe http://effbot.org/zone/element-xpath.htm). ElementPath unterstützt nicht die Suche nach Elementen, die eine bestimmte Zeichenfolge enthalten.

Warum verwenden Sie nicht XPath? Unter der Annahme, dass die Datei test.xml enthält Ihre XML-Beispiel, die folgenden Werke:

> python 
Python 2.7.9 (default, Jun 29 2016, 13:08:31) 
[GCC 4.9.2] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 

>>> from lxml import etree 
>>> tree=etree.parse("test.xml") 
>>> tree.xpath("Confirmation[starts-with(TransactionId, 'GTEREVIEW')]") 
[<Element Confirmation at 0x7f68b16c3c20>] 

Wenn Sie darauf bestehen, findall() verwenden, das Beste, was Sie tun können, ist die Liste aller Confirmation Elemente erhalten einen TransactionId Kind-Knoten mit:

>>> tree.findall("Confirmation[TransactionId]") 
[<Element Confirmation at 0x7f68b16c3c20>, <Element Confirmation at 0x7f68b16c3ea8>] 

Sie müssen dann manuell diese Liste filtern, zB:

>>> [e for e in tree.findall("Confirmation[TransactionId]") 
    if e[0].text.startswith('GTEREVIEW')] 
[<Element Confirmation at 0x7f68b16c3c20>] 

Wenn Ihr Dokument con tains Namespaces, wird folgendes Sie alle einen Knoten TransactionId Kind mit Confirmation Elemente erhalten, vorausgesetzt, dass die Elemente, die die Standard-Namespace verwenden (verwendete ich xmlns="file:xyz" als Standard-Namespace):

>>> tree.findall("//{{{0}}}Confirmation[{{{0}}}TransactionId]".format(tree.getroot().nsmap[None])) 
[<Element {file:xyz}Confirmation at 0x7f534a85d1b8>, <Element {file:xyz}Confirmation at 0x7f534a85d128>] 

Und es gibt natürlich etree.ETXPath:

>>> find=etree.ETXPath("//{{{0}}}Confirmation[starts-with({{{0}}}TransactionId, 'GTEREVIEW')]".format(tree.getroot().nsmap[None])) 
>>> find(tree) 
[<Element {file:xyz}Confirmation at 0x7f534a85d1b8>] 

Dies ermöglicht die Kombination von XPath und Namespaces.

+0

Vielen Dank für Ihre Antwort! Leider ist in meinem Dokument ein Namespace enthalten, der dazu führt, dass der Xpath eine leere Liste zurückgibt. Nach dem Entfernen des Namespace aus der Datei scheint der Code zu funktionieren. Gibt es einen Weg dahin? Die Datei beginnt im Wesentlichen mit Und endet mit naiminp

+0

Dachte so. Sie können dann den zweiten Ansatz mit 'findall()' verwenden. Sie müssen nur die zurückgegebene Knotenliste filtern. – Markus

0
//Confirmation[TransactionId[contains(.,'GTEREVIEW')]] 


father_tag[child_tag] # select father_tag that has child_tag 
[child_tag[filter]] # select select child tag which match filter 
[filter] 
+0

fügen Sie einfach eine Erklärung hinzu –