Parsing großer XML-Dateien mit Ruby & Nokogiri

7

Ich habe eine große XML-Datei (ungefähr 10K Zeilen), die ich regelmäßig in diesem Format parsen muss:

%Vor%

Was ich tun möchte, ist, jeden einzelnen Knoten mit nokogiri zu analysieren, um die Anzahl der Objekte in einer Kategorie zu zählen. Dann möchte ich diese Zahl von total_count subtrahieren, um eine Ausgabe zu bekommen, die liest "Count of Interest_Category: n, Zählung von allem anderen: z".

Das ist jetzt mein Code:

%Vor%

Das scheint zu funktionieren, ist aber sehr langsam! Ich spreche mehr als 10 Minuten für 10.000 Artikel. Gibt es einen besseren Weg, dies zu tun? Tue ich etwas in einer nicht optimalen Weise?

    
DNadel 14.05.2012, 17:26
quelle

5 Antworten

3

Sie können die Ausführungszeit erheblich verkürzen, indem Sie Ihren Code wie folgt ändern. Ändern Sie einfach die "99" in die Kategorie, die Sie überprüfen möchten.:

%Vor%

Dies dauerte ungefähr drei Sekunden an meinem Gerät. Ich denke, ein Schlüsselfehler, den Sie gemacht haben, war, dass Sie die "Items" iteriert haben, anstatt eine Sammlung der Item-Knoten zu erstellen. Das hat Ihren Iterationscode umständlich und langsam gemacht.

    
vlasits 14.05.2012, 19:20
quelle
23

Hier ist ein Beispiel zum Vergleich einer SAX-Parser-Zählung mit einer DOM-basierten Zählung, die 500.000 <item> s mit einer von sieben Kategorien zählt. Zuerst die Ausgabe:

  

Erstellen Sie eine XML-Datei: 1.7s
  Zählen über SAX: 12.9s
  Erstellen DOM: 1.6s
  Zählen über DOM: 2.5s

Beide Techniken erzeugen den gleichen Hashwert und zählen die Anzahl der gesehenen Kategorien:

%Vor%

Die SAX-Version benötigt 12,9 Sekunden zum Zählen und Kategorisieren, während die DOM-Version nur 1,6 Sekunden benötigt, um die DOM-Elemente zu erstellen und 2,5 Sekunden mehr, um alle <cat> -Werte zu finden und zu kategorisieren. Die DOM-Version ist etwa 3x so schnell!

... aber das ist nicht die ganze Geschichte. Wir müssen uns auch die RAM-Nutzung ansehen.

  • Für 500.000 Artikel erreicht SAX (12.9s) einen Spitzenwert von 238MB RAM; DOM (4,1s) erreicht einen Spitzenwert von 1,0 GB.
  • Für 1.000.000 Artikel erreicht SAX (25.5s) einen Spitzenwert von 243 MB RAM; DOM (8.1s) erreicht einen Spitzenwert von 2.0GB.
  • Für 2.000.000 Artikel erreicht SAX (55.1s) Spitzenwerte von 250 MB RAM; DOM ( ??? ) erreicht einen Spitzenwert von 3,2 GB.

Ich hatte genug Speicher auf meinem Computer, um 1.000.000 Elemente zu verwalten, aber bei 2.000.000 lief mir der Arbeitsspeicher aus und ich musste anfangen, virtuellen Speicher zu verwenden. Selbst mit einer SSD und einer schnellen Maschine ließ ich den DOM-Code fast zehn Minuten lang laufen, bevor er ihn schließlich tötete.

Es ist sehr wahrscheinlich, dass die langen Zeiten, die Sie berichten, darauf zurückzuführen sind, dass Ihnen der Arbeitsspeicher knapp wird und Sie die Festplatte kontinuierlich als Teil des virtuellen Speichers nutzen. Wenn Sie das DOM in den Speicher einbauen können, verwenden Sie es, da es FAST ist. Wenn Sie jedoch nicht können, müssen Sie wirklich die SAX-Version verwenden.

Hier ist der Testcode:

%Vor%

Wie funktioniert das DOM-Zählen?

Wenn wir einen Teil der Teststruktur entfernen, sieht der DOM-basierte Zähler wie folgt aus:

%Vor%

Wie funktioniert das SAX-Zählen?

Zuerst konzentrieren wir uns auf diesen Code:

%Vor%

Wenn wir eine neue Instanz dieser Klasse erstellen, erhalten wir ein Objekt mit einem Hash, der für alle Werte standardmäßig 0 ist, und ein paar Methoden, die darauf aufgerufen werden können. Der SAX-Parser ruft diese Methoden auf, während er durch das Dokument läuft.

  • Jedes Mal, wenn der SAX-Parser ein neues Element sieht, ruft er die Methode start_element für diese Klasse auf. Wenn das passiert, setzen wir ein Flag, basierend darauf, ob dieses Element den Namen "cat" hat oder nicht (damit wir den Namen später finden können).

  • Jedes Mal, wenn der SAX-Parser ein Stück Text aufschlitzt, ruft er die Methode characters unseres Objekts auf. Wenn das passiert, überprüfen wir, ob das letzte Element, das wir gesehen haben, eine Kategorie war (d. H. Wenn @count auf true gesetzt wurde); Wenn ja, verwenden wir den Wert dieses Textknotens als Kategorienamen und fügen einen zu unserem Zähler hinzu.

Um unser benutzerdefiniertes Objekt mit dem SAX-Parser von Nokogiri zu verwenden, machen wir folgendes:

%Vor%     
Phrogz 15.05.2012 02:48
quelle
3

Ich würde empfehlen, einen SAX-Parser anstelle eines DOM-Parsers für eine so große Datei zu verwenden. Nokogiri hat einen schönen SAX-Parser eingebaut: Ссылка

Die SAX-Methode ist gut für große Dateien, einfach weil sie keinen riesigen DOM-Baum erzeugt, was in Ihrem Fall zu viel ist; Sie können eigene Strukturen aufbauen, wenn Ereignisse ausgelöst werden (z. B. zum Zählen von Knoten).

    
Eric Wood 14.05.2012 19:10
quelle
0

Schau dir Greg Webers Version von Paul Dix 'Saxophon-Juwel an: Ссылка

Parsing große Datei mit SaxMachine scheint die gesamte Datei in den Speicher zu laden

Sax-Maschine macht den Code viel einfacher; Gregs Variante lässt es streamen.

    
Martin Cleaver 03.08.2013 13:23
quelle
0

können Sie das ausprobieren - Ссылка

HugeXML.read xml, elements_lookup do |element| # => element{ :name, :value, :attributes} end

Ich habe auch versucht, ox

zu verwenden     
Amol Pujari 29.05.2012 02:01
quelle

Tags und Links