使用Python将CSV转换为支持mongoimport的JSON
我有一个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一样的编码?我处于完全停滞状态。
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。
Petri,谢谢,它工作!你是最棒的!是否有可能以与CSV相同的方式排序输出,并将geonameid字段保存为数字,而不是将其作为字符串?该脚本将引号添加到geonameid字段。 – Karl
更新了该示例,以便将geonameid编码为一个数字。订单在这里真的很重要,还是你只是为了自己的目的而追求完美? :)您可以切换到使用常规csv.reader,首先阅读标题行:'headers = reader.next()',然后使用它为每一行生成有序字典,即。 'r = OrderedDict(zip(headers,row))'。试一试,我相信你可以让它工作。 – Petri
我注意到替代名称字段在查询中工作速度很慢,因为整个字段被视为单个字符串。如果将替代名称分别放在引号内并将字段设置为数组,则搜索会更快。 该字段将如下所示: '备用名称:[“Zamin Sukhteh”,“ZamīnSūkhteh”]' 是否有可能通过Python更新解决方案?我认为任何人将geonames数据库转换为MongoDB可能会发现这一点更好,因为该字段的查询目前不可能。 – Karl
也许你可以尝试直接导入CSV到MongoDB的使用
mongoimport -d <dB> -c <collection> --type csv --file location.csv --headerline
这种方法在我的一台服务器上为我节省了相当多的内存v。运行一个首先读取.csv文件的python脚本。 – andrewwowens
我很高兴听到:-) –
可能的重复:http://stackoverflow.com/questions/1884395/csv-to-json-script – xiaoyi
我的问题是关于格式和不是错误消息。我没有得到任何错误,但没有得到所需的输出。 – Karl
这个问题不是重复的:在上面提到的另一个问题中,不存在编码问题和特殊输出格式要求。 – Petri