2015-04-01 8 views
7

Ich versuche, OpenStreetMap planet.osm zu analysieren, komprimiert im bz2-Format. Da es bereits 41G ist, möchte ich die Datei nicht vollständig dekomprimieren.Analysieren einer großen .bz2-Datei (40 GB) mit lxml iterparse in Python. Fehler, der nicht bei unkomprimierter Datei angezeigt wird

So habe ich herausgefunden, wie Teile der planet.osm Datei

from lxml import etree as et 
from bz2 import BZ2File 

path = "where/my/fileis.osm.bz2" 
with BZ2File(path) as xml_file: 
    parser = et.iterparse(xml_file, events=('end',)) 
    for events, elem in parser: 

     if elem.tag == "tag": 
      continue 
     if elem.tag == "node": 
      (do something) 


    ## Do some cleaning 
    # Get rid of that element 
    elem.clear() 

    # Also eliminate now-empty references from the root node to node   
    while elem.getprevious() is not None: 
     del elem.getparent()[0] 

mit dem folgenden Code mit bz2 und lxml, zu analysieren, die perfekt mit den Geofabrick extracts arbeitet. Allerdings, wenn ich versuche, die Planeten-latest.osm.bz2 mit dem gleichen Skript zu analysieren erhalte ich die Fehlermeldung:

xml.etree.XMLSyntaxError: Specification mandate value for attribute num_change, line 3684, column 60

Hier sind die Dinge, die ich versuchte:

  • die Planeten-latest prüfen. osm.bz2 md5sum
  • Überprüfen Sie den Planeten-last.osm, wo das Skript mit bz2 stoppt. Es gibt keinen offensichtlichen Fehler, und das Attribut heißt "num_changes", nicht "num_change" wie im Fehler
  • angezeigt. Ich tat auch etwas Dummes, aber der Fehler verwirrte mich: Ich öffnete den Planeten-last.osm.bz2 in Modus 'rb' [c = BZ2File ('file.osm.bz2', 'rb')] und dann c.read() an iterparse() übergeben, was mir einen Fehler zurückgab, der besagt, dass (sehr lange Zeichenfolge) nicht geöffnet werden kann. Merkwürdig, (sehr lange Zeichenfolge) endet genau da, wo der „Specification Mandat Wert“ Fehler bezieht sich ...

Dann zuerst die planet.osm.gz2 zu dekomprimieren Ich habe versucht, ein einfaches

usin
bzcat planet.osm.gz2 > planet.osm 

Und lief der Parser direkt auf planet.osm. Und es hat funktioniert! Ich bin sehr verwirrt darüber und konnte keinen Hinweis darauf finden, warum dies passieren könnte und wie ich dies lösen könnte. Ich vermute, dass zwischen Dekompression und Parsing etwas passiert, aber ich bin mir nicht sicher. Bitte hilf mir zu verstehen!

+0

nicht sicher, natürlich sagen kann, aber die 'BZ2File (file.osm.bz2 , 'rb') 'sieht falsch aus, da das erste Argument gemäß den Dokumenten ein _Dateiname_ (dh eine Zeichenkette) sein soll. – martineau

+0

Vielen Dank, dass Sie darauf hingewiesen haben! Aber es war korrekt im ursprünglichen Code, ich habe gerade meine Frage bearbeitet, um Verwirrung zu vermeiden. – scities

+1

Es könnte einen Fehler im bz2-Modul geben (ich bezweifle, dass es von den Betreuern sehr oft an 40-GB-Eingängen getestet wird). Versuchen Sie, ein Python-Skript zu schreiben, das das bz2-Modul verwendet, um die Daten zu dekomprimieren und in eine neue Datei zu schreiben, und vergewissern Sie sich, dass die Ausgabe mit der Ausgabe von 'bzcat' übereinstimmt. –

Antwort

5

Es stellt sich heraus, dass das Problem mit der komprimierten Datei planet.osm auftritt.

Wie auf der OSM Wiki angedeutet, wird der Planet-Datei als und das bz2 Python-Modul kann nicht lesen Multistream-Dateien Datei Multistream komprimiert. Die bz2-Dokumentation weist jedoch auf ein alternatives Modul hin, das solche Dateien lesen kann: bz2file. Ich habe es benutzt und es funktioniert perfekt!

So sollte der Code lesen:

from lxml import etree as et 
from bz2file import BZ2File 

path = "where/my/fileis.osm.bz2" 
with BZ2File(path) as xml_file: 
    parser = et.iterparse(xml_file, events=('end',)) 
    for events, elem in parser: 

     if elem.tag == "tag": 
      continue 
     if elem.tag == "node": 
      (do something) 


    ## Do some cleaning 
    # Get rid of that element 
    elem.clear() 

    # Also eliminate now-empty references from the root node to node   
    while elem.getprevious() is not None: 
     del elem.getparent()[0] 

Auch über die Verwendung der PBF-Format einige der Forschung (in den Kommentaren geraten), stieß ich auf imposm.parser, ein Python-Modul, das eine generische Parser für OSM implementiert Daten (im pbf- oder xml-Format). Vielleicht möchten Sie sich das ansehen!

2

Als Alternative können Sie die Ausgabe bzcat Befehl verwendet werden können (die auch Multistream-Dateien verarbeiten kann):

p = subprocess.Popen(["bzcat", "data.bz2"], stdout=subprocess.PIPE) 
parser = et.iterparse(p.stdout, ...) 
# at the end just check that p.returncode == 0 so there were no errors