2012-11-20 14 views
5

Ich habe eine 300 mb CSV mit 3 Millionen Zeilen im Wert von Stadtinformationen von Geonames.org. Ich versuche, diese CSV in JSON zu konvertieren, um mit Mongoimport in MongoDB zu importieren. Der Grund, warum ich JSON möchte, ist, dass ich das Feld "loc" als Array und nicht als String für den Geospatial-Index angeben kann. Die CSV ist in UTF-8 codiert.Convert CSV in Mongoimport-freundliche JSON mit Python

Ein Ausschnitt meiner CSV sieht wie folgt aus:

"geonameid","name","asciiname","alternatenames","loc","feature_class","feature_code","country_code","cc2","admin1_code","admin2_code","admin3_code","admin4_code" 
3,"Zamīn Sūkhteh","Zamin Sukhteh","Zamin Sukhteh,Zamīn Sūkhteh","[48.91667,32.48333]","P","PPL","IR",,"15",,, 
5,"Yekāhī","Yekahi","Yekahi,Yekāhī","[48.9,32.5]","P","PPL","IR",,"15",,, 
7,"Tarvīḩ ‘Adāī","Tarvih `Adai","Tarvih `Adai,Tarvīḩ ‘Adāī","[48.2,32.1]","P","PPL","IR",,"15",,, 

Die gewünschte JSON-Ausgang (mit Ausnahme des charset), die mit mongoimport arbeitet unten:

{"geonameid":3,"name":"Zamin Sukhteh","asciiname":"Zamin Sukhteh","alternatenames":"Zamin Sukhteh,Zamin Sukhteh","loc":[48.91667,32.48333] ,"feature_class":"P","feature_code":"PPL","country_code":"IR","cc2":null,"admin1_code":15,"admin2_code":null,"admin3_code":null,"admin4_code":null} 
{"geonameid":5,"name":"Yekahi","asciiname":"Yekahi","alternatenames":"Yekahi,Yekahi","loc":[48.9,32.5] ,"feature_class":"P","feature_code":"PPL","country_code":"IR","cc2":null,"admin1_code":15,"admin2_code":null,"admin3_code":null,"admin4_code":null} 
{"geonameid":7,"name":"Tarvi? ‘Adai","asciiname":"Tarvih `Adai","alternatenames":"Tarvih `Adai,Tarvi? ‘Adai","loc":[48.2,32.1] ,"feature_class":"P","feature_code":"PPL","country_code":"IR","cc2":null,"admin1_code":15,"admin2_code":null,"admin3_code":null,"admin4_code":null} 

Ich habe versucht, alle verfügbaren Online-CSV -JSON-Konverter und sie funktionieren nicht wegen der Dateigröße. Der nächste, den ich bekam, war mit Mr Data Converter (der oben abgebildete), der nach dem Entfernen der Anfangs- und Endklammer und Kommas zwischen Dokumenten in MongoDb importieren würde. Leider funktioniert dieses Tool nicht mit einer 300 MB Datei.

Der obige JSON ist so eingestellt, dass er in UTF-8 kodiert ist, aber immer noch Zeichensatzprobleme hat, wahrscheinlich aufgrund eines Konvertierungsfehlers?

Ich verbrachte die letzten drei Tage damit, Python zu lernen, Python-CSVKIT zu verwenden, alle CSV-JSON-Skripts auf Stackoverflow zu testen, CSV in MongoDB zu importieren und "loc" -String in Array zu ändern (dabei werden die Anführungszeichen leider beibehalten) sogar versucht, manuell 30.000 Datensätze gleichzeitig zu kopieren und einzufügen. Viel Reverse Engineering, Versuch und Irrtum und so weiter.

Hat jemand eine Ahnung, wie man das JSON oben erreicht, während man die Kodierung wie in der CSV oben behält? Ich bin vollkommen still.

+0

möglich Duplikate mit: http://stackoverflow.com/questions/1884395/csv-to-json-script – xiaoyi

+0

Meine Frage ist in Bezug auf die Formatierung und keine Fehlermeldungen. Ich bekomme keine Fehler, aber weder die gewünschte Ausgabe. – Karl

+1

Diese Frage ist kein Duplikat: Es gibt sowohl Codierungsprobleme als auch spezielle Ausgabeformatanforderungen, die in der anderen oben genannten Frage nicht vorhanden sind. – Petri

Antwort

9

Python-Standardbibliothek (plus simplejson für Dezimal-Codierung Unterstützung) hat alles, was Sie brauchen:

import csv, simplejson, decimal, codecs 

data = open("in.csv") 
reader = csv.DictReader(data, delimiter=",", quotechar='"') 

with codecs.open("out.json", "w", encoding="utf-8") as out: 
    for r in reader: 
     for k, v in r.items(): 
     # make sure nulls are generated 
     if not v: 
      r[k] = None 
     # parse and generate decimal arrays 
     elif k == "loc": 
      r[k] = [decimal.Decimal(n) for n in v.strip("[]").split(",")] 
     # generate a number 
     elif k == "geonameid": 
      r[k] = int(v) 
     out.write(simplejson.dumps(r, ensure_ascii=False, use_decimal=True)+"\n") 

Wo "in.csv" Ihre große CSV-Datei enthält. Der obige Code ist getestet als Arbeiten an Python 2.6 & 2.7, mit etwa 100 MB CSV-Datei, die eine ordnungsgemäß codierte UTF-8-Datei. Ohne umgebende Klammern, Array-Quoting oder Komma-Trennzeichen, wie gewünscht.

Es ist auch erwähnenswert, dass die Übergabe der Parameter use_ascii und use_decimal erforderlich ist, damit die Codierung ordnungsgemäß funktioniert (in diesem Fall).

Schließlich based on simplejson, wird die python stdlib json Paket auch früher oder später Unterstützung der dezimalen Codierung. Es wird also nur die stdlib benötigt.

+0

Petri, danke, es hat funktioniert! Du bist der beste! Ist es möglich, die Ausgabe auf die gleiche Weise wie die CSV zu sortieren und das Geonameid-Feld als Zahl zu behalten, anstatt es als String zu definieren? Das Skript fügte dem geonameid-Feld Anführungszeichen hinzu. – Karl

+0

Das Beispiel wurde aktualisiert, sodass Geonameid auch in eine Zahl kodiert wird.Spielt die Ordnung hier eine Rolle, oder streben Sie nur nach Perfektion um ihrer selbst willen? :) Sie können zu einem regulären csv.reader wechseln, lesen Sie zuerst die Kopfzeile: 'headers = reader.next()' und verwenden Sie diese später, um ein geordnetes Wörterbuch für jede Zeile zu erzeugen, dh. 'r = OrderedDict (zip (headers, row))'. Probieren Sie es aus, ich bin sicher, Sie können es zum Laufen bringen. – Petri

+0

Ich habe festgestellt, dass das Feld für alternative Namen bei Abfragen sehr langsam ist, da das gesamte Feld als einzelne Zeichenfolge behandelt wird. Die Suche wäre viel schneller, wenn die alternativen Namen in Anführungszeichen gesetzt würden und das Feld zu einem Array gemacht würde. Das Feld würde wie folgt aussehen: 'alternatenames: [ "Zamin Sukhteh", "Zamīn Sūkhteh"]' Ist es möglich, die Lösung zu aktualisieren, dies mit Python geschieht? Ich denke, dass jeder, der die Geonames-Datenbank in MongoDB konvertiert, dies möglicherweise besser findet, da Abfragen auf dem Feld derzeit nicht möglich sind. – Karl

3

Vielleicht versuchen Sie könnten die csv direkt in mongodb Import

mongoimport -d <dB> -c <collection> --type csv --file location.csv --headerline 
+0

Dieser Ansatz hat mir ziemlich viel Speicher auf einem meiner Server gespart. V. Ein Python-Skript ausgeführt, das zuerst die .csv-Datei gelesen hat. – andrewwowens

+0

Ich bin sehr froh, das zu hören :-) –