Python SAX-Parser gegen Python DOM-Parser. Parsim FIAS-HĂ€user

In einem frĂŒheren Artikel haben wir einen Ansatz zum Erstellen von CSV aus XML in einer von FIAS veröffentlichten Datenbank betrachtet. Die Analyse basierte auf dem DOM-Parser, der die gesamte Datei vor der Verarbeitung in den Speicher lĂ€dt, was dazu fĂŒhrte, dass große Dateien angesichts der begrenzten RAM-GrĂ¶ĂŸe aufgeteilt werden mussten. Dieses Mal wird empfohlen, zu prĂŒfen, wie gut der SAX-Parser ist, und seine Geschwindigkeit mit dem DOM-Parser zu vergleichen. Die grĂ¶ĂŸte der FIAS-Datenbankdateien, HĂ€user mit einer GrĂ¶ĂŸe von 27,5 GB, wird als Testperson verwendet.

Eintrag


Wir sind gezwungen, die angesehenste Öffentlichkeit sofort zu verĂ€rgern - versĂ€umen es sofort, die Datenbankdatei der FIX-HĂ€user dem SAX-Parser zuzufĂŒhren. Der Parser stĂŒrzt mit dem Fehler "nicht wohlgeformt (ungĂŒltiges Token)" ab. Und anfangs gab es den Verdacht, dass die Datenbankdatei kaputt war. Nach dem Zerlegen der Datenbank in mehrere kleine Teile wurde jedoch festgestellt, dass die Abweichungen durch eine geĂ€nderte Codierung fĂŒr Hausnummern und / oder GebĂ€ude verursacht wurden. Das heißt, die STRUCNUM- oder HOUSENUM-Tags stießen auf HĂ€user mit einem Buchstaben, der in einer seltsamen Codierung geschrieben war (nicht UTF-8 und nicht ANSI, in dem das Dokument selbst erstellt wurde):



Wenn diese Codierung durch AusfĂŒhren der Datei ĂŒber die Funktion remove_non_ascii korrigiert wird, hat der Datensatz gleichzeitig die folgende Form:



Eine solche Datei wurde aufgrund zusÀtzlicher Zeichen auch nicht vom Parser absorbiert.

Ich musste mich an regulĂ€re AusdrĂŒcke erinnern und die Datei bereinigen, bevor ich sie in den Parser lud.
Frage: Warum es unmöglich ist, eine normale Datenbank zu erstellen, die fĂŒr die Arbeit ausgelegt ist, erhĂ€lt einen rhetorischen Farbton.

Um die Startfunktionen der Parser auszurichten, entfernen wir das Testfragment von den obigen Inkonsistenzen.

Code zum Löschen der Datenbankdatei vor dem Laden in den Parser:

Code
from datetime import datetime import re from unidecode import unidecode start = datetime.now() f= open('AS_HOUSE.462.xml', 'r',encoding='ANSI') def remove_non_ascii(text): return unidecode(unidecode(text)) for line in f: b=remove_non_ascii(line) for c in re.finditer(r'\w{5}NUM="\d{1,}\"\w\"',b): print(c[0]) c1=c[0][:-3]+c[0][-2] print(c1) b=b.replace(c[0],c1) #    #  f1= open('out.xml', 'w',encoding='ANSI') f1.write(b) f1.close() f.close() print(datetime.now()- start) 


Der Code ĂŒbersetzt non_ascii-Zeichen in der XML-Datei in normal und entfernt dann das unnötige "" in den Namen von GebĂ€uden und HĂ€usern.

Sax Parser


Nehmen Sie zunĂ€chst eine kleine XML-Datei (58,8 MB). Wir planen, txt oder csv an der Ausgabe abzurufen, was fĂŒr die weitere Verarbeitung in Pandas oder Excel praktisch ist.

Code
 import xml.sax import csv from datetime import datetime start = datetime.now() class EventHandler(xml.sax.ContentHandler): def __init__(self,target): self.target = target def startElement(self,name,attrs): self.target.send(attrs._attrs.values()) def characters(self,text): self.target.send('') def endElement(self,name): self.target.send('') def coroutine(func): def start(*args,**kwargs): cr = func(*args,**kwargs) cr.__next__() return cr return start with open('out.csv', 'a') as f: # example use if __name__ == '__main__': @coroutine def printer(): while True: event = (yield) print(event,file=f) xml.sax.parse("out.xml", EventHandler(printer())) print(datetime.now()- start) 


Nach dem AusfĂŒhren des Programms erhalten wir die Werte des Python-Wörterbuchs:



Vorlaufzeit: 5-6 Sek.

DOM-Parser


Wir verarbeiten dieselbe Datei, indem wir sie zuerst vollstÀndig in den Speicher laden. Dies ist genau die Methode, die der DOM-Parser verwendet.

Code
 import codecs,os import xml.etree.ElementTree as ET import csv from datetime import datetime parser = ET.XMLParser(encoding="ANSI") tree = ET.parse("out.xml",parser=parser) root = tree.getroot() Resident_data = open('AS_HOUSE.0001.csv', 'a',encoding='ANSI') csvwriter = csv.writer(Resident_data) attr_names = [ 'HOUSEID', 'HOUSEGUID', 'AOGUID', 'HOUSENUM', 'STRUCNUM', 'STRSTATUS', 'ESTSTATUS', 'STATSTATUS', 'IFNSFL', 'IFNSUL', 'TERRIFNSFL', 'TERRIFNSUL', 'OKATO', 'OKTMO', 'POSTALCODE', 'STARTDATE', 'ENDDATE', 'UPDATEDATE', 'COUNTER', 'NORMDOC', 'DIVTYPE', 'REGIONCODE' ] start = datetime.now() object = [] for member in root.findall('House'): object = [member.attrib.get(attr_name, None) for attr_name in attr_names] csvwriter.writerow(object) Resident_data.close() print(datetime.now()- start) 


Vorlaufzeit 2-3 Sek.
Einen DOM-Parser gewinnen?

GrĂ¶ĂŸere Dateien


Kleine Dateien spiegeln die RealitĂ€t nicht vollstĂ€ndig wider. Nehmen wir eine Datei, die grĂ¶ĂŸer als 353 MB ist (nach der Reinigung, wie oben angegeben).

Schulterergebnisse:

SAX-Parser: 0: 00: 32.090836 - 32 Sek
DOM-Parser: 0: 00: 16.630951 - 16 Sek

Der Unterschied betrĂ€gt das Zweifache der Geschwindigkeit. Dies beeintrĂ€chtigt jedoch nicht den Hauptvorteil des SAX-Parsers - die FĂ€higkeit, große Dateien zu verarbeiten, ohne sie zuvor in den Speicher zu laden.
Es bleibt zu bedauern, dass dieser Vorteil nicht auf die FIAS-Datenbank anwendbar ist, da Vorarbeiten mit Codierungen erforderlich sind.

Datei zur vorlÀufigen Reinigung von Codierungen:
- 353 MB im Archiv .

Gereinigte DB-Datei fĂŒr Parser-Tests:
- 353 MB im Archiv .

Source: https://habr.com/ru/post/de469995/


All Articles