2012-11-20 15 views
5

Mam 300 CSB CSV z informacją o wartości 3 milionów wierszy od Geonames.org. Próbuję przekonwertować ten plik CSV na JSON, aby zaimportować go do MongoDB za pomocą mongoimport. Powodem, dla którego chcę JSON jest to, że pozwala mi to określić pole "loc" jako tablicę, a nie ciąg do użycia z indeksem geoprzestrzennym. Plik CSV jest kodowany w UTF-8.Konwersja CSV do przyjaznego dla mongoimport JSON przy użyciu Pythona

fragment mojego CSV wygląda tak:

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

Pożądana wyjście JSON (z wyjątkiem zestawu znaków), który współpracuje z mongoimport jest poniżej:

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

Próbowałem wszystkie dostępne CSV internetowego -JSON konwertery i nie działają ze względu na rozmiar pliku. Najbliżej znajdowałem się z Mr Data Converter (ten na zdjęciu powyżej), który został zaimportowany do MongoDb po usunięciu nawiasu początkowego i końcowego oraz przecinków między dokumentami. Niestety to narzędzie nie działa z plikiem 300 MB.

Powyższy JSON jest zakodowany w UTF-8, ale nadal ma problemy z zestawem znaków, najprawdopodobniej z powodu błędu konwersji?

Spędziłem ostatnie trzy dni nauki Pythona, starając się używać Pythona CSVKIT, próbując wszystkie skrypty CSV JSON na StackOverflow, importu CSV do MongoDB i zmiana „Loc” ciąg do tablicy (ten zachowuje cudzysłowy niestety) i nawet próbuje ręcznie skopiować i wkleić 30 000 rekordów jednocześnie. Wiele inżynierii wstecznej, prób i błędów i tak dalej.

Czy ktoś ma wskazówkę, jak osiągnąć powyższy JSON, zachowując kodowanie jak w powyższym CSV? Jestem całkowicie unieruchomiony.

+0

możliwe duplikaty: http://stackoverflow.com/questions/1884395/csv-to-json-script – xiaoyi

+0

Moje pytanie dotyczy formatowania i nie komunikaty o błędach. Nie otrzymuję żadnych błędów, ale nie otrzymuję pożądanych wyników. – Karl

+1

To pytanie nie jest duplikatem: w drugim pytaniu, o którym mowa powyżej, nie ma problemów z kodowaniem ani specjalnych wymagań dotyczących formatu wyjściowego. – Petri

Odpowiedz

9

Python biblioteki standardowej (plus simplejson dla dziesiętnych wsparcie kodowania) ma wszystko, czego potrzeba:

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

Gdzie "in.csv" zawiera swój duży plik CSV. Powyższy kod jest testowany jako działający na Pythonie 2.6 & 2.7, z plikiem csv o wielkości około 100MB, tworząc poprawnie zakodowany plik UTF-8. Bez otaczających nawiasów, cytowania tablic ani ograniczników przecinków, zgodnie z żądaniem.

Warto również zauważyć, że podanie parametrów secure_ascii i use_decimal jest wymagane do prawidłowego działania kodowania (w tym przypadku).

Wreszcie, jako based on simplejson, pakiet python stdlib json zyska również obsługę kodowania dziesiętnego wcześniej lub później. Tak więc ostatecznie będzie potrzebna tylko stdlib.

+0

Petri, dziękuję, zadziałało! Jesteś najlepsza! Czy można zamówić dane wyjściowe w taki sam sposób, jak plik CSV i zachować pole geonameid jako numer zamiast tworzyć ciąg znaków? Skrypt dodał cytaty do pola geonameid. – Karl

+0

Zaktualizowano przykład, więc geonameid jest również zakodowany w liczbę.Czy porządek naprawdę ma tu znaczenie, czy po prostu dążysz do doskonałości ze względu na nią samą? :) Możesz przełączyć się do używania zwykłego csv.reader, najpierw przeczytaj wiersz nagłówka: 'headers = reader.next()', a następnie użyj tego do wygenerowania uporządkowanego słownika dla każdego wiersza, tj. 'r = OrderedDict (zip (nagłówki, wiersz))'. Wypróbuj to, jestem pewien, że możesz sprawić, żeby działało. – Petri

+0

Zauważyłem, że pole alternatywnych nazw działa bardzo wolno z zapytaniami, ponieważ całe pole jest traktowane jako pojedynczy ciąg. Wyszukiwanie byłoby znacznie szybsze, gdyby alternatywne nazwy były umieszczane w cudzysłowach, a pole zostało przekształcone w tablicę. Pole będzie wyglądać tak: 'alternatenames: [" Zamin Sukhteh "," Zamīn Sūkhteh "]' Czy można zaktualizować rozwiązanie, aby stało się to za pomocą Pythona? Myślę, że każdy, kto konwertuje bazę danych geonezji na MongoDB, może znaleźć to lepiej, ponieważ zapytania na tym polu nie są obecnie możliwe. – Karl

3

Być może można spróbować zaimportować CSV bezpośrednio do MongoDB użyciu

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

takie podejście zaoszczędziło mi całkiem sporo pamięci na jednym z moich serwerów, v. Uruchomieniu skryptu Pythona, który najpierw odczytuje plik .csv. – andrewwowens

+0

Bardzo się cieszę, że to słyszę :-) –

Powiązane problemy