2012-04-05 10 views
2

Mein Ziel ist es, 25 GB XML-Daten zu parsen. Ein Beispiel für einen solchen Daten ist unten angegeben:Effizientes XML-Parsing für 25 GB Daten

<Document> 
<Data Id='12' category='1' Body="abc"/> 
<Data Id='13' category='1' Body="zwq"/> 
. 
. 
<Data Id='82018030' category='2' CorrespondingCategory1Id='13' Body="pqr"/> 

However..considering die Daten, die ich von „25 GB“ haben ... mein Ansatz recht ineffizient ist. Bitte schlagen Sie einen Weg vor, um meinen Code oder einen alternativen Ansatz zu verbessern. Fügen Sie bitte einen kleinen Beispielcode hinzu, um die Dinge klarer zu machen.

+0

mögliche Duplikate von [Python-Saxophon zu Lxml für 80 + GB XML] (http://StackOverflow.com/Questions/9809469/Python-Sax-To-Lxml-For-80Gb-Xml) –

Antwort

4

Sie könnten feststellen, dass ein SAX-Parser für diese Aufgabe besser geeignet ist. Anstatt einen DOM zu erstellen, wandelt ein SAX-Parser die XML-Datei in einen Strom von Elementen um und ruft Funktionen auf, die Sie zur Verfügung stellen, damit Sie jedes Element behandeln können.

Die gute Sache ist, dass SAX-Parser sehr schnell und Speicher-effizient im Vergleich zu DOM-Parsern sein können, und einige müssen nicht einmal alle XML auf einmal gegeben werden, was ideal wäre, wenn Sie 25 GB haben es.

Leider, wenn Sie alle Kontextinformationen benötigen, wie „Ich will Tag <B> aber nur, wenn es <A> innen-Tag ist,“ Sie müssen es selbst pflegen, da alle Parser geben Sie „Start-Tag <A>, Starttag <B> sind, end tag <B>, end tag <A>. " Es sagt Ihnen nie explizit, dass das Tag <B> innerhalb des Tags <A> ist, Sie müssen das herausfinden, was Sie gesehen haben. Und wenn du einmal ein Element gesehen hast, ist es weg, es sei denn, du erinnerst dich selbst daran.

Dies wird sehr haarig für komplexe Parsing-Jobs, aber Ihre ist wahrscheinlich überschaubar.

Es kommt vor, dass Pythons Standardbibliothek einen SAX-Parser in xml.sax hat. Sie möchten wahrscheinlich etwas wie xml.sax.xmlreader.IncrementalParser.

+0

Danke für die Hilfe. Kannst du bitte das Konzept mit einem kleinen Beispielcode erklären ... das wäre wirklich nett von dir ... du könntest auch dein eigenes einfaches Beispiel konstruieren. –

+0

Wenn ich eine Chance bekomme, werde ich. Ich bin mir aber nicht sicher, ob ich Zeit haben werde. – kindall

0

Mein erster Vorschlag aus dem Blick auf Ihre Frage wäre die Verwendung einer relationalen Datenbank wie MySQL oder SQLite. Es wäre nicht schwer, Ihre XML-Daten in diese Form zu bringen, und die Abfrage dieser Daten wäre sowohl einfacher als auch schneller.

+0

Ich will nicht um diesen Ansatz zu verfolgen ... im Grunde braucht die Verbindung der Tabellen auch für 25 GB Daten viel Zeit. Therefore..Ich bin auf der Suche nach einer Alternative mit Parsing selbst –

+0

Für die Art der Abfrage, die Sie hier mit so vielen Daten machen, brauchen Sie wirklich eine Datenbank. Wenn Sie die Indizes richtig definieren, wird es * viel * schneller und einfacher als das, was Sie vorschlagen. Verwenden Sie daher SAX oder einen anderen inkrementellen Parser, um die Daten in eine Datenbank zu laden, und fragen Sie dann von der Datenbank ab, um in eine neue XML-Datei zu schreiben. –

0

Ihr erster Algorithmus läuft in O (n^2), was für 25 GB Daten sehr langsam ist. Im Idealfall werden Sie es auf O (n) oder O (n log n) bringen. In Ermangelung einer anderen Informationen über die Daten (wie, ob die Kategorie 1 oder 2 ist kleiner, usw.), können Sie etwas tun (die O (n)):

from lxml import objectify 
f=open('myfile25GB', 'r') 
text=f.read() 
root=objectify.fromstring(text) 

cat_one_bodies = {} 
for e in root.attrib['Document'].row: 
    category = int(e.attrib['category']) 
    body = e.attrib['Body'] 
    if category == 1: 
     e_id = int(e.attrib['Id']) 
     cat_one_bodies[e_id] = body 
    else: #Assuming there are only 2 categories 
     cat_one_id = int(e.attrib['CorrespondingCategory1Id']) 
     print "Cat1 Body: '%s' Cat2 Body: '%s'" % (body, cat_one_bodies[cat_one_id]) 

Obwohl dies nicht parse deine Datei, hoffentlich zeigt es dir die Idee. Es verwendet möglicherweise ziemlich viel Speicher (da es alle Körper der Kategorie 1 im Wörterbuch verwaltet), so dass dies eine Überlegung sein kann.

0

In XSLT 3.0 (Entwurf), wie sie derzeit in Saxon-EE implementiert, können Sie eine Streaming-Transformation schreiben, die dieses Problem löst, wie folgt:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"> 
<xsl:mode streamable="yes"/> 
<xsl:template match="/"> 
    <xsl:iterate select="Document/Data"> 
    <xsl:param name="map" select="map{}"/> 
    <xsl:choose> 
     <xsl:when test="@category='1'"> 
     <xsl:next-iteration> 
      <xsl:with-param name="map" select="map:put($map, string(@Id), string(@Body))"/> 
     </xsl:next-iteration> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="'Cat1 Body: ', 
           $map(@CorrespondingCategoryId), 'Cat2 Body', @Body"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:iterate> 
</xsl:template> 

ich nicht getestet haben (es ist Spät in der Nacht am Vorabend eines viertägigen Urlaubs ...) aber wenn Sie daran interessiert sind, diesen Ansatz zu verfolgen, werde ich Ihnen gerne helfen. XSLT 3.0 ist immer noch eine Entwurfsspezifikation und ziemlich flüssig. Der Schwerpunkt liegt auf der Lösung von Problemen wie diesem, bei dem ein Streaming-Ansatz verwendet wird, der sehr große Dokumente mit beschränktem Speicher verarbeitet. Saxon-EE 9.4 implementiert eine Momentaufnahme der Spezifikation.

+0

Das sieht ziemlich süß aus. – kindall

0

Wenn sich die IDs in aufsteigender Reihenfolge befinden, können Sie eine eigene Funktion implementieren, die ein Element an einer beliebigen Position in einer Datei liest.Dann können Sie einfach die gesamte Datei scannen und für jedes Element können Sie das entsprechende mit dem binären Suchalgorithmus finden. Das Ding wird in O (n log n) laufen, während immer noch eine vernachlässigbare Speichermenge verwendet wird.

0

Versuchen Sie, Iterparse aus Lxml zu verwenden. Ich denke, dass es dem Problem entspricht, das Sie behandeln möchten.

+0

Hier ist eine ähnliche Frage gelöst von .iterparse() http://stackoverflow.com/a/9814580/1346705 – pepr