XML-Analyse - ElementTree vs SAX und DOM

74

Python bietet verschiedene Möglichkeiten, XML zu analysieren ...

Ich verstehe die Grundlagen des Parsens mit SAX . Es fungiert als Stream-Parser mit einer ereignisgesteuerten API.

Ich verstehe auch den DOM- Parser. Es liest das XML in den Speicher und konvertiert es in Objekte, auf die mit Python zugegriffen werden kann.

Im Allgemeinen war es einfach, zwischen beiden zu wählen, je nachdem, was Sie tun mussten, Speicherbeschränkungen, Leistung usw.

(Hoffentlich bin ich soweit richtig.)

Seit Python 2.5 haben wir auch ElementTree . Wie ist das im Vergleich zu DOM und SAX? Welchem ​​ist es ähnlicher? Warum ist es besser als die vorherigen Parser?

Corey Goldberg
quelle

Antworten:

72

ElementTree ist viel einfacher zu verwenden, da es einen XML-Baum (im Grunde genommen) als Listenstruktur darstellt und Attribute als Wörterbücher dargestellt werden.

ElementTree benötigt für XML-Bäume viel weniger Speicher als DOM (und ist daher schneller), und der Parsing-Overhead über iterparseist mit SAX vergleichbar. Gibt außerdem iterparseTeilstrukturen zurück, und Sie können die Speichernutzung während des Parsens konstant halten, indem Sie die Strukturen verwerfen, sobald Sie sie verarbeiten.

ElementTree verfügt wie in Python 2.5 nur über einen kleinen Funktionsumfang im Vergleich zu vollständigen XML-Bibliotheken, reicht jedoch für viele Anwendungen aus. Wenn Sie einen validierenden Parser oder eine vollständige XPath-Unterstützung benötigen, ist lxml der richtige Weg. Früher war es ziemlich instabil, aber seit 2.1 hatte ich keine Probleme mehr damit.

ElementTree weicht von DOM ab, wo Knoten Zugriff auf ihre Eltern und Geschwister haben. Der Umgang mit tatsächlichen Dokumenten anstelle von Datenspeichern ist ebenfalls etwas umständlich, da Textknoten nicht als tatsächliche Knoten behandelt werden. Im XML-Snippet

<a>This is <b>a</b> test</a>

Die Zeichenfolge testwird das sogenannte tailvon-Element sein b.

Im Allgemeinen empfehle ich ElementTree als Standard für die gesamte XML-Verarbeitung mit Python und DOM oder SAX als Lösung für bestimmte Probleme.

Torsten Marek
quelle
2
Vielen Dank, dass Sie die beiden folgenden Einschränkungen erwähnt haben! (Ich brauche zufällig beides in meinem Projekt.) "XPath-Unterstützung ... ElementTree weicht von DOM ab, wo Knoten Zugriff auf ihre Eltern und Geschwister haben."
Jon Coombs
1
ElementTree hat auch das Problem, dass Textinhalte als eine Eigenschaft des vorhergehenden Knotens behandelt werden, nicht als ein eigener Knoten. In "<p> Der <i> braune </ i> Hund </ p>" hat das <p> -Element 1 Kind, nicht 3. "Das" ist eine Eigenschaft für <p> und "Hund". ist eine Eigenschaft für <i> (nicht einmal dieselbe Eigenschaft - möglicherweise wird auch direkt nach dem Ende von <p> Text angezeigt). Fast alles an Bäumen, Kontexten, Pfaden und Suchen funktioniert anders als Sie es gewohnt sind, wenn Sie HTML, CSS oder fast alles andere verwenden, was mit Dokumenten zu tun hat.
TextGeek
13

Minimale DOM-Implementierung:

Link .

Python bietet eine vollständige W3C-Standardimplementierung von XML DOM ( xml.dom ) und eine minimale xml.dom.minidom . Letzteres ist einfacher und kleiner als die vollständige Implementierung. Aus einer "Parsing-Perspektive" hat es jedoch alle Vor- und Nachteile des Standard-DOM - dh es lädt alles in den Speicher.

Betrachten einer grundlegenden XML-Datei:

<?xml version="1.0"?>
<catalog>
    <book isdn="xxx-1">
      <author>A1</author>
      <title>T1</title>
    </book>
    <book isdn="xxx-2">
      <author>A2</author>
      <title>T2</title>
    </book>
</catalog>

Ein möglicher Python-Parser mit Minidom ist:

import os
from xml.dom import minidom
from xml.parsers.expat import ExpatError

#-------- Select the XML file: --------#
#Current file name and directory:
curpath = os.path.dirname( os.path.realpath(__file__) )
filename = os.path.join(curpath, "sample.xml")
#print "Filename: %s" % (filename)

#-------- Parse the XML file: --------#
try:
    #Parse the given XML file:
    xmldoc = minidom.parse(filepath)
except ExpatError as e:
    print "[XML] Error (line %d): %d" % (e.lineno, e.code)
    print "[XML] Offset: %d" % (e.offset)
    raise e
except IOError as e:
    print "[IO] I/O Error %d: %s" % (e.errno, e.strerror)
    raise e
else:
    catalog = xmldoc.documentElement
    books = catalog.getElementsByTagName("book")

    for book in books:
        print book.getAttribute('isdn')
        print book.getElementsByTagName('author')[0].firstChild.data
        print book.getElementsByTagName('title')[0].firstChild.data

Beachten Sie, dass xml.parsers.expat eine Python-Schnittstelle zum nicht validierenden Expat-XML-Parser ist (docs.python.org/2/library/pyexpat.html).

Das xml.dom- Paket enthält auch die Ausnahmeklasse DOMException , wird jedoch nicht in minidom unterstützt !

Die ElementTree XML-API:

Link .

ElementTree ist viel einfacher zu verwenden und benötigt weniger Speicher als XML DOM. Darüber hinaus ist eine C-Implementierung verfügbar ( xml.etree.cElementTree ).

Ein möglicher Python-Parser mit ElementTree ist:

import os
from xml.etree import cElementTree  # C implementation of xml.etree.ElementTree
from xml.parsers.expat import ExpatError  # XML formatting errors

#-------- Select the XML file: --------#
#Current file name and directory:
curpath = os.path.dirname( os.path.realpath(__file__) )
filename = os.path.join(curpath, "sample.xml")
#print "Filename: %s" % (filename)

#-------- Parse the XML file: --------#
try:
    #Parse the given XML file:
    tree = cElementTree.parse(filename)
except ExpatError as e:
    print "[XML] Error (line %d): %d" % (e.lineno, e.code)
    print "[XML] Offset: %d" % (e.offset)
    raise e
except IOError as e:
    print "[XML] I/O Error %d: %s" % (e.errno, e.strerror)
    raise e
else:
    catalogue = tree.getroot()

    for book in catalogue:
        print book.attrib.get("isdn")
        print book.find('author').text
        print book.find('title').text
Paolo Rovelli
quelle
2
Vielen Dank! Sehr hilfreich. Ich bin mir nicht sicher genug, um es zu bearbeiten, aber ich denke, (a) das andere ist nicht hilfreich, da es kein endgültiges gibt: stackoverflow.com/questions/855759/python-try-else ; (b) eine einfache Erhöhung würde mehr als eine Erhöhung bewahren e: stackoverflow.com/questions/11420464/…
Jon Coombs
Zu Punkt (a) ja. Es gibt keine endgültige Aussage, nur weil in meinem Beispiel keine Notwendigkeit bestand. Ich erinnere mich nicht, warum ich es gesagt habe. Selbst wenn dies in diesem Fall nutzlos ist, ist die else-Anweisung nicht syntaktisch falsch.
Paolo Rovelli
In Bezug auf Punkt (b) könnte dies der Fall sein. Ich denke jedoch (in meinem Beispiel), dass dies etwas außerhalb des Rahmens liegt. In der Tat sollte der Code nur ein einfaches Beispiel für XML-Parsing sein ...
Paolo Rovelli
1
Oh, ich meinte nicht, dass irgendetwas davon 'falsch' war; nur einige Änderungsvorschläge für andere, die mitkommen und kopieren / einfügen könnten.
Jon Coombs
5
Beachten Sie, dass der cElementTree ab Python 3.3
gerrit
8

ElementTree hat mehr pythonische API. Es befindet sich jetzt auch in der Standardbibliothek, sodass durch die Verwendung Abhängigkeiten reduziert werden.

Eigentlich bevorzuge ich lxml, da es eine API wie ElementTree hat, aber auch nette zusätzliche Funktionen hat und eine gute Leistung erbringt.

iny
quelle
7

ElementTrees parse () ist wie DOM, während iterparse () wie SAX ist. Meiner Meinung nach ist ElementTree besser als DOM und SAX, da es eine API bietet, mit der man einfacher arbeiten kann.

Sanxiyn
quelle
Außerdem finde ich, dass ich die reale Struktur will, nicht eine Reihe von Ereignissen.
S.Lott
1
Ein serieller Parser ist oft gut genug für einfaches Parsen. Ich habe Python mit Saxophon gestartet und erst dann zu Minidom gewechselt, wenn meine Anforderungen für Saxophon zu komplex wurden. Ich sollte hinzufügen, dass ich ElementTree noch nicht verwendet habe, da es nicht genügend mehr Funktionen zu bieten scheint, um meinen Code darauf zu portieren.
Golday