2008-11-27 18 views
45

Ich bin zur Zeit der folgende Code basiert auf Kapitel 12.5 des Python-Kochbuch:Was ist der schnellste Weg, um große XML-Dokumente in Python zu analysieren?

from xml.parsers import expat 

class Element(object): 
    def __init__(self, name, attributes): 
     self.name = name 
     self.attributes = attributes 
     self.cdata = '' 
     self.children = [] 
    def addChild(self, element): 
     self.children.append(element) 
    def getAttribute(self,key): 
     return self.attributes.get(key) 
    def getData(self): 
     return self.cdata 
    def getElements(self, name=''): 
     if name: 
      return [c for c in self.children if c.name == name] 
     else: 
      return list(self.children) 

class Xml2Obj(object): 
    def __init__(self): 
     self.root = None 
     self.nodeStack = [] 
    def StartElement(self, name, attributes): 
     element = Element(name.encode(), attributes) 
     if self.nodeStack: 
      parent = self.nodeStack[-1] 
      parent.addChild(element) 
     else: 
      self.root = element 
     self.nodeStack.append(element) 
    def EndElement(self, name): 
     self.nodeStack.pop() 
    def CharacterData(self,data): 
     if data.strip(): 
      data = data.encode() 
      element = self.nodeStack[-1] 
      element.cdata += data 
    def Parse(self, filename): 
     Parser = expat.ParserCreate() 
     Parser.StartElementHandler = self.StartElement 
     Parser.EndElementHandler = self.EndElement 
     Parser.CharacterDataHandler = self.CharacterData 
     ParserStatus = Parser.Parse(open(filename).read(),1) 
     return self.root 

ich mit XML-Dokumente über 1 GB groß arbeite. Kennt jemand eine schnellere Möglichkeit, diese zu parsen?

+2

Ihre Frage ist viel zu vage alle nützlichen Antworten aufzulesen. Erwägen Sie, diese Fragen zu beantworten: - Was versuchen Sie mit diesem 1 GB XML-Dokument zu tun? - Wie schnell brauchen Sie diesen Parser? - Können Sie das Dokument durchforsten, anstatt alles von Anfang an in den Speicher zu laden? – Matt

+2

Ich muss alles in den Speicher laden, indizieren Sie die Daten und dann 'durchsuchen' und verarbeiten Sie es. –

Antwort

52

Ich sehe für mich aus, als ob Sie keine DOM-Funktionen von Ihrem Programm benötigen. Ich würde die Verwendung der (c) ElementTree-Bibliothek als Zweites verwenden. Wenn Sie die iterparse-Funktion des cElementTree-Moduls verwenden, können Sie sich durch das XML-Dokument arbeiten und mit den Ereignissen fertig werden, sobald sie auftreten.

Hinweis jedoch Fredriks Beratung über celementtree mit iterparse function:

, um große Dateien zu analysieren, können Sie so schnell los Elemente erhalten, wie Sie haben sie verarbeitet:

for event, elem in iterparse(source): 
    if elem.tag == "record": 
     ... process record elements ... 
     elem.clear() 

Das obige Muster hat einen Nachteil; Das Wurzelelement wird nicht gelöscht. Sie erhalten also ein einzelnes Element mit vielen leeren untergeordneten Elementen. Wenn Ihre Dateien groß sind und nicht nur groß sind, könnte dies ein Problem darstellen. Um dies zu umgehen, müssen Sie das Wurzelelement in die Hände bekommen. Der einfachste Weg, dies zu tun ist Startereignisse zu ermöglichen, und einen Verweis auf das erste Element in einer Variablen speichern:

# get an iterable 
context = iterparse(source, events=("start", "end")) 

# turn it into an iterator 
context = iter(context) 

# get the root element 
event, root = context.next() 

for event, elem in context: 
    if event == "end" and elem.tag == "record": 
     ... process record elements ... 
     root.clear() 

Die lxml.iterparse() dies nicht zulässt.

4

Die Registrierung von Callbacks verlangsamt das Parsen enorm. [EDIT] Das liegt daran, dass der (schnelle) C-Code den Python-Interpreter aufrufen muss, der nicht so schnell wie C ist. Im Grunde verwenden Sie den C-Code, um die Datei zu lesen (schnell) und dann das DOM in Python zu erstellen (langsam). [/ EDIT]

Versuchen Sie, xml.etree.ElementTree zu verwenden, das zu 100% in C implementiert ist und XML ohne Rückfragen an Python-Code analysieren kann.

Nachdem das Dokument analysiert wurde, können Sie es filtern, um zu erhalten, was Sie wollen.

Wenn das immer noch zu langsam ist und Sie kein DOM benötigen, besteht eine andere Möglichkeit darin, die Datei in eine Zeichenfolge zu lesen und einfache String-Operationen zu verwenden, um sie zu verarbeiten.

+0

Dies ist ein sehr irreführender Hinweis. Es gibt nichts über einen Callback-basierten XML-Parser, der an sich langsam ist. Darüber hinaus verwendet das OP bereits Pythons Expat-Bindungen, die ebenfalls native C sind. – Matt

+0

Der Python-Interpreter ist immer langsamer als nativ kompilierter C-Code. Und wie Sie im Code in der Frage deutlich sehen können, wird Python-Code für jedes Element aufgerufen! Und dieser Code macht auch eine Menge Arbeit! –

+0

Dies sollte erhöht werden, Callbacks in Python sind wirklich langsam, Sie wollen das vermeiden und so viel wie möglich in C landen. –

8

Ich empfehle Ihnen, lxml zu verwenden, es ist eine Python-Bindung für die libxml2-Bibliothek, die wirklich schnell ist.

In meiner Erfahrung haben libxml2 und Expat sehr ähnliche Leistung. Aber ich bevorzuge libxml2 (und lxml für Python), weil es aktiver entwickelt und getestet zu sein scheint. Auch libxml2 hat mehr Funktionen.

lxml ist meist API-kompatibel mit xml.etree.ElementTree. Und es gibt eine gute Dokumentation auf seiner Website.

+2

lxml ist die Regel! :) – ddaa

15

Haben Sie das cElementTree-Modul ausprobiert?

cElementTree ist in Python 2.5 und höher als xml.etree.cElementTree enthalten. Siehe die benchmarks.

entfernt tot Images Link

+0

das Bild zeigt nicht: ( – fedorqui

4

Wenn Ihre Anwendungsleistung empfindlich und wahrscheinlich große Dateien begegnen (wie Sie, die> 1 GB), dann würde ich stark abraten den Code mit du bist zeigt in Ihrer Frage aus dem einfachen Grund, dass es das gesamte Dokument in RAM lädt. Ich würde Sie ermutigen, Ihr Design (wenn überhaupt möglich) zu überdenken, um zu vermeiden, den gesamten Dokumentenbaum auf einmal im RAM zu halten. Da ich nicht weiß, was die Anforderungen Ihrer Anwendung sind, kann ich keinen spezifischen Ansatz vorschlagen, außer dem allgemeinen Ratschlag, ein "ereignisbasiertes" Design zu verwenden.

0

Anscheinend PyRXP ist wirklich schnell.

Sie behaupten, es ist der schnellste Parser - aber cElementTree ist nicht in ihrer Statistikliste.

1

Expat ParseFile gut funktioniert, wenn Sie den gesamten Baum im Speicher nicht gespeichert werden müssen, die früher oder später wird Ihr RAM für große Dateien blasen:

import xml.parsers.expat 
parser = xml.parsers.expat.ParserCreate() 
parser.ParseFile(open('path.xml', 'r')) 

Es liest die Dateien in Stücke, und führt sie dem Parser zu, ohne RAM zu explodieren.

Doc: https://docs.python.org/2/library/pyexpat.html#xml.parsers.expat.xmlparser.ParseFile

Verwandte Themen