XML effizient nach Schlüssel-Wert-Paaren durchsuchen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
XCDRiVER
User
Beiträge: 31
Registriert: Sonntag 12. Februar 2017, 19:59

Hallo,

ich suche eine effiziente Methode um aus den Attributen der Elemente bestimmte Daten auszulesen.
Im Moment bin ich an dieser Stelle angekommen.
Erst mal die Struktur der XML Datei:

[codebox=xml file=Unbenannt.xml]
<data show="------">
<section unit="A" unitTrigger="001" unitTargetIp="10.0.0.11" unitTargetPort="51000">
<event>
<trigger name="" value="666" />
<target ip="192.168.0.5" port="6000" command="abc" value="75" />
</event>
<event>
<trigger name="switch_05" value="1" />
<target ip="192.168.0.5" port="6000" command="efg" value="10" />
</event>
<event>
<trigger name="" value="666" />
<target ip="192.168.0.18" port="8800" command="hij" value="1" />
</event>
</section>

<section unit="B" unitTrigger="002" unitTargetIp="10.0.0.11" unitTargetPort="50000">
<event>
<trigger name="" value="555" />
<target ip="" port="" command="lmn" value="100" />
</event>
<event>
<trigger name="" value="666" />
<target ip="" port="" command="opq" value="50" />
</event>
</section>
</data>[/code]


Mit dem nachfolgenden Python-Schnipsel möchte ich einen Wert, hier z.B. 666
aus dem Element 'trigger' Attribute 'value' finden.
Das soll so Ressourcen schonend wie 'sinnvoll möglich' gehen.
Es ist so,
es kommen etwa 150 Trigger-Werte/Sekunde in das Programm und ich möchte eine Vorauswahl treffen ob die Weiterverarbeitung der
Daten notwendig ist, also, ob eine Reaktion (hinterlegt in XML Element <target>) auf diese Werte erfolgen soll.
Ich möchte also Rechenleistung sparen.
Gibt es einen besseren (dennoch lesbareren) Weg, das zu realisieren?

Code: Alles auswählen

import xml.etree.ElementTree as ET

tree = ET.parse('test.xml')
root = tree.getroot()

if __name__ == "__main__":

## SUCHE NACH value SO KURZ WIE GEHT
    i = 0
    for child in root:
        for werte in root[i].iter('trigger'):
            if (werte.get('value') == "666"):
                print(i)
        i += 1
Vielen Dank für Eure Hinweise
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du schon "child" hast, wozu das gemurkse mit i & root ? Das ist doch das child.

Und "iter('trigger')" kannst du doch gleich auf Root aufrufen, da eh rekursiv (und das nutzt du ja schon)

Last but not least: wenn du Kontrolle über den Prozess hast, der diese xml Daten generiert - hör auf xml zu benutzen. Ereignisse und schließende Tags vertragen sich schlecht. Entweder du generierst lauter kleine xml snippets, dann brauchst du den Aufriss einer hierarchischen Auszeichnung nicht. Oder du schreibst ein großes Dokument - dann ist das so lange ungültig wie nicht das schließende root-Tag kommt.
BlackJack

@XCDRiVER: Ich würde es ja erst einmal richtig machen, bevor so effizient wie möglich kommt, und wenn man es ganz normal löst, auch erst einmal schauen ob das überhaupt zu langsam ist.

Was bei Deinem Code auffällt ist a) das `child` überhaupt nicht verwendet wird, und b) das ``root`` ja sicher irgendwann zu einer Ausnahme führen wird, denn es wird da ja nicht unendlich viele Elemente geben.

Ansonsten ist die Aufteilung des Code zwischen Modulebene und ``if __name__ …``-Zweig unsinnig. Das gehört dort alles rein. Besser noch in eine Funktion.

Bevor man am eigenen Quelltext rumbastelt, würde ich erst einmal schauen ob `lxml.etree` den gewünschten Geschwindigkeitszuwachs bringt. Das hat grundsätzlich die gleiche API, kann aber beispielsweise auch XPath und man kann von Knoten aus zum Elternknoten navigieren:

Code: Alles auswählen

from lxml import etree
 

def main():
    tree = etree.parse('test.xml')
    for trigger_node in tree.xpath('.//trigger[@value=$value]', value='666'):
        print(trigger_node.getparent().find('target').attrib)
 

if __name__ == "__main__":
    main()
Benutzeravatar
XCDRiVER
User
Beiträge: 31
Registriert: Sonntag 12. Februar 2017, 19:59

@ __deets__

danke für deine Hinweise, ich habe den Schnipsel mal so abgeändert wie ich das verstanden habe
Es funktioniert.
an dem Dateiformat xml wollte ich erst mal noch nicht rütteln, es sollte ein Format sein, was von Menschenhand editierbar und durchschaubar ist.
Mir ist da als erstes xml eingefallen.
Auf jeden Fall ist der Code kürzer und übersichtlicher so.

Code: Alles auswählen

import xml.etree.ElementTree as ET

tree = ET.parse('test.xml')
root = tree.getroot()

if __name__ == "__main__":

    for werte in root.iter('trigger'):
        if (werte.get('value') == "666"):
            print("data found")
Benutzeravatar
XCDRiVER
User
Beiträge: 31
Registriert: Sonntag 12. Februar 2017, 19:59

@BlackJack,
ich denke, so ist das erst mal ganz brauchbar.
muss mir aber die lxml Bibliothek später anschauen,
weil ich vorerst versuche, mit der Standardbibliothek auszukommen.
danke dir sehr
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

XCDRiVER hat geschrieben:@ __deets__
an dem Dateiformat xml wollte ich erst mal noch nicht rütteln, es sollte ein Format sein, was von Menschenhand editierbar und durchschaubar ist.
Mir ist da als erstes xml eingefallen.
Ein klassisches zeilenbasiertes Format wie zB Apache logfiles haben ist auch Menschen-les - und -änderbar, aber robuster und passender.

Und du solltest BlackJacks Kritik an der Codeverteilung außerhalb und innerhalb des __main__-Blocks beherzigen. So ist das Sinn- weil wirkungslos.
BlackJack

Wenn man es etwas strukturierter haben möchte, bietet sich JSON Lines oder YAML an.
Benutzeravatar
XCDRiVER
User
Beiträge: 31
Registriert: Sonntag 12. Februar 2017, 19:59

Danke sehr,
zukünftig bestimmt denkbar,
aber im Moment muss ich bei xml bleiben.
viele Grüße
Antworten