2012-11-20 17 views
5

Ho un CSV da 300 mb con 3 milioni di righe di informazioni sulla città da Geonames.org. Sto cercando di convertire questo CSV in JSON per importarlo in MongoDB con mongoimport. La ragione per cui voglio JSON è che mi permette di specificare il campo "loc" come una matrice e non una stringa da usare con l'indice geospaziale. Il CSV è codificato in UTF-8.Converti CSV in JSON mongoimport-friendly usando Python

Un frammento del mio CSV si presenta così:

"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",,, 

L'uscita JSON desiderato (tranne il set di caratteri) che funziona con mongoimport è qui sotto:

{"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} 

Ho provato tutti i CSV disponibile online -JSON convertitori e non funzionano a causa delle dimensioni del file. Il più vicino che ho ottenuto è stato con Mr Data Converter (quello nella foto sopra) che importerebbe in MongoDb dopo aver rimosso la parentesi iniziale e finale e le virgole tra i documenti. Sfortunatamente quello strumento non funziona con un file da 300 mb.

Il JSON precedente è impostato per essere codificato in UTF-8 ma ha ancora problemi di charset, molto probabilmente a causa di un errore di conversione?

Ho trascorso gli ultimi tre giorni imparando Python, provando a usare Python CSVKIT, provando tutti gli script CSV-JSON su StackOverflow, importando CSV in MongoDB e modificando la stringa "loc" su array (questo mantiene le virgolette purtroppo) e anche cercando di copiare e incollare manualmente 30.000 record alla volta. Un sacco di ingegneria inversa, prove ed errori e così via.

Qualcuno ha la minima idea di come ottenere il JSON sopra mantenendo la codifica corretta come nel CSV sopra? Sono completamente fermo.

+0

possibili duplicati: formattazione http://stackoverflow.com/questions/1884395/csv-to-json-script – xiaoyi

+0

La mia domanda è per quanto riguarda e non messaggi di errore. Non ricevo errori, ma nemmeno l'output desiderato. – Karl

+1

Questa domanda non è un duplicato: ci sono sia problemi di codifica che requisiti di output speciali non presenti nell'altra domanda sopra citata. – Petri

risposta

9

libreria standard di Python (più simplejson per il supporto codifica decimale) ha tutto ciò che serve:

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") 

Dove "in.csv" contiene il file csv grande. Il codice sopra è testato come funziona su Python 2.6 & 2.7, con circa 100 MB di file csv, producendo un file UTF-8 correttamente codificato. Senza parentesi, quotazioni di array e delimitatori di virgola, come richiesto.

Vale anche la pena notare che è necessario il passaggio dei parametri sure_ascii e use_decimal perché la codifica funzioni correttamente (in questo caso).

Infine, essendo based on simplejson, il pacchetto python stdlib json avrà anche il supporto per la codifica decimale prima o poi. Quindi alla fine sarà necessario solo lo stdlib.

+0

Petri, grazie, ha funzionato! Sei il migliore! È possibile ordinare l'output allo stesso modo del CSV e conservare il campo del geonameide come un numero invece di renderlo una stringa? Lo script aggiunge citazioni al campo geonameid. – Karl

+0

Aggiornato l'esempio in modo che geonameid sia codificato anche in un numero.L'ordine conta davvero qui, o stai solo mirando alla perfezione fine a se stessa? :) Puoi passare ad usare un normale csv.reader, prima leggi la riga di intestazione: 'headers = reader.next()' e poi usalo per generare un dizionario ordinato per ogni riga, es. 'r = OrderedDict (zip (intestazioni, riga))'. Provalo, sono sicuro che puoi farlo funzionare. – Petri

+0

Ho notato che il campo dei nomi alternativi funziona molto lentamente con le query perché l'intero campo è trattato come una singola stringa. La ricerca sarebbe molto più veloce se i nomi alternativi fossero tutti racchiusi tra virgolette e il campo fosse trasformato in un array. Il campo sarebbe simile a questo: 'alternatenames: [" Zamin Sukhteh "," Zamīn Sūkhteh "]' E 'possibile aggiornare la soluzione per fare in modo che questo accada con Python? Penso che chiunque sia in grado di convertire il database dei geonames in MongoDB potrebbe trovarlo meglio perché al momento non sono possibili query sul campo. – Karl

3

Forse si potrebbe provare l'importazione del csv direttamente in MongoDB usando

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

questo approccio mi ha salvato un bel po 'di memoria su uno dei miei server v. Eseguendo uno script python che leggesse prima il file .csv. – andrewwowens

+0

Sono molto contento di sentirlo :-) –