2012-11-20 64 views
5

我有一個300 MB的CSV,包含300萬行來自Geonames.org的城市信息。我正在嘗試將這個CSV轉換爲JSON,並通過mongoimport導入到MongoDB中。我想要JSON的原因是,它允許我將「loc」字段指定爲數組,而不是用於地理空間索引的字符串。 CSV以UTF-8編碼。使用Python將CSV轉換爲支持mongoimport的JSON

我的CSV的片段看起來是這樣的:

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

所需的JSON輸出(除字符集)與mongoimport的工作原理是下面:

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

我已經嘗試了所有在網上提供CSV -JSON轉換器,並且由於文件大小而無法工作。我得到的最接近的是Mr Data Converter(上圖所示),它將在刪除文檔之間的開始和結束括號以及逗號之後導入MongoDb。不幸的是,該工具不適用於300 MB的文件。

上面的JSON被設置爲UTF-8編碼,但仍然有charset問題,最有可能是由於轉換錯誤?我嘗試使用Python CSVKIT,嘗試使用stackoverflow上的所有CSV-JSON腳本,將CSV導入到MongoDB並將「loc」字符串更改爲數組(不幸保留了引號),並嘗試使用Python CSVKIT甚至嘗試一次手動複製和粘貼30,000條記錄。很多逆向工程,試驗和錯誤等等。

有沒有人有線索如何實現上面的JSON,同時保持像上面的CSV一樣的編碼?我處於完全停滯狀態。

+0

可能的重複:http://stackoverflow.com/questions/1884395/csv-to-json-script – xiaoyi

+0

我的問題是關於格式和不是錯誤消息。我沒有得到任何錯誤,但沒有得到所需的輸出。 – Karl

+1

這個問題不是重複的:在上面提到的另一個問題中,不存在編碼問題和特殊輸出格式要求。 – Petri

回答

9

Python標準庫(加上simplejson十進制編碼的支持)擁有所有你需要:

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

其中 「in.csv」 包含你的大csv文件。上面的代碼已經過測試,正在使用Python 2.6 & 2.7,大約有100MB csv文件,生成一個正確編碼的UTF-8文件。根據要求,不包括括號,數組引號或逗號分隔符。

還值得注意的是,傳遞ensure_ascii和use_decimal參數是編碼正常工作所必需的(在這種情況下)。

最後,作爲based on simplejson,python stdlib json包遲早也會獲得十進制編碼支持。所以最終只需要stdlib。

+0

Petri,謝謝,它工作!你是最棒的!是否有可能以與CSV相同的方式排序輸出,並將geonameid字段保存爲數字,而不是將其作爲字符串?該腳本將引號添加到geonameid字段。 – Karl

+0

更新了該示例,以便將geonameid編碼爲一個數字。訂單在這裏真的很重要,還是你只是爲了自己的目的而追求完美? :)您可以切換到使用常規csv.reader,首先閱讀標題行:'headers = reader.next()',然後使用它爲每一行生成有序字典,即。 'r = OrderedDict(zip(headers,row))'。試一試,我相信你可以讓它工作。 – Petri

+0

我注意到替代名稱字段在查詢中工作速度很慢,因爲整個字段被視爲單個字符串。如果將替代名稱分別放在引號內並將字段設置爲數組,則搜索會更快。 該字段將如下所示: '備用名稱:[「Zamin Sukhteh」,「ZamīnSūkhteh」]' 是否有可能通過Python更新解決方案?我認爲任何人將geonames數據庫轉換爲MongoDB可能會發現這一點更好,因爲該字段的查詢目前不可能。 – Karl

3

也許你可以嘗試直接導入CSV到MongoDB的使用

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

這種方法在我的一臺服務器上爲我節省了相當多的內存v。運行一個首先讀取.csv文件的python腳本。 – andrewwowens

+0

我很高興聽到:-) –