GAE中如何将数据批量上传到Datastore中呢?使用GAE提供的bulkload_client.py工具,但是不能上传中文数据,一上传中文数据就出错,幸好K_Reverter写了一篇好文将包含中文的数据上传到Google app engine,不过K兄说的是简单,但整个过程却不容易,整了一个晚上,勉强把数据上传上去,现在写此文,算是对K兄文章的详解吧,对像我这样的菜菜会有点用。
请先下载我调试完的实例吧:https://files.cnblogs.com/Tangf/CSVupload2GAEdemo.rar
OK,我们具体来说步骤(开始之前建议先看一下Bulk Data Uploader一文,我也是先按该文来做的,但我讲的话会按照我的思路来讲)。
#上传数据
bulkload_client.py工具(安装完GAE SDK1.1.9以后会在Google App Engine目录下直接找到)是用于上传CSV数据的客户端,上传语法为:bulkload_client.py --filename CSV文件路径 --kind Datastore类(差不多可以理解为一个表,而这个表是由若干个字段组成) –url GAE接口地址。从上传语法来看,仅仅靠客户端肯定是完成不了上传的,因为GAE本身不提供直接的接口给你,而你的字段的组织GAE也是不知道,你必须要告诉它。所以,我们要做一个URL地址并考虑一下字段。
在实例包中有一个poi.csv的样例数据,主要有4个字段,分别是纬度、精度、英文名、中文名四个,我们以此为例来做一个URL的接口吧。做一个upload.py的文件,作为URL接口,代码和说明如下:
import bulkload #注意此处不是from google.appengine.ext import bulkload,由于处理不了Unicode,所以我们不使用Google提供的bulkload,而是采用了我们修改了的bulkload,放在根目录下,我们就可以调用它了。(后面会对此文件的修改进行说明)
from google.appengine.api import datastore_types
from google.appengine.ext import search
class PersonLoader(bulkload.Loader):
def __init__(self):
# 定义一个PointList的实体(类似于表),包含了Lat、Lon、EN、Name共四个字段,由于Name是中文字段,所以采用了unicode类型,而其余是数字或英文,所以采用str类型,如果中文字段不设置成unicode类型则上传会出错。
bulkload.Loader.__init__(self, 'PointList',
[('Lat', str),
('Lon', str),
('EN', str),
('Name', unicode),
])
def HandleEntity(self, entity):
ent = search.SearchableEntity(entity)
return ent
if __name__ == '__main__':
bulkload.main(PersonLoader())
这样的话接口文件就完成了,其中PointList实体就是bulkload_client.py上传时需要的Kind参数,我们同时将该文件定义一个URL路径,在app.yaml中的handlers:下面添加
- url: /upload
script: upload.py
表示路径为/upload然后交给upload.py来处理。
这样的话整个语句上传语句就是:C:\Program Files\Google\google_appengine>bulkload_client.py –filename poi.csv --kind PointList --url http://127.0.0.1:8080/upload --debug
同时需要说明的是:poi.csv文件必须为UTF-8编码,才能完成上传;yaml中并没有做admin登录要求,所以安全性需要考虑。
整个数据上传工作就完成了。
#检验数据
上传完数据做一下检验吧。我在yaml文件中加入了
- url: /myadmin/.*|/myadmin
script: $PYTHON_LIB/google/appengine/ext/admin
login: admin
语句,意思是使用/myadmin/可以登录后台查看Datastore。但不知道为什么,list出PointList实体的时候提示出错,连Google自己的后台对Unicode编码也存在问题(郁闷吧),懒的去找原因(怀疑也是Unicode编码问题),查看不了就想想其他办法吧。重新写一个view.py文件,该文件通过查找EN字段输出Name中文字段,代码和说明如下:
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext import search
class MainPage(webapp.RequestHandler):
def get(self):
# 关键字查询,可以在URL的后面增加“?keyword=英文关键字”可以进行查询
keyword = self.request.get('keyword')
self.response.headers['Content-Type'] = 'text/plain'
if not keyword:
self.response.out.write("No keyword has been set")
else:
# 查询的是PointList实体(上传的时候就是这个实体,已经在Datastore中了)
query = search.SearchableQuery('PointList')
query.Search(keyword)
for result in query.Run():
self.response.out.write('%s' % result['Name']) #返回中文名称Name字段
def main():
application = webapp.WSGIApplication([('/', MainPage)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == "__main__":
main()
在app.yaml文件中增加
- url: /.*
script: view.py
表示/后面的全部交给view.py来处理,而如果路径带有“?keyword=theBund”这样的关键字,则输出“外滩”。
最终能够输出中文,表示CSV文件上传正确。
#如何支持中文字
根据from google.appengine.ext import bulkload此句,可见bulkload类位于:C:\Program Files\Google\google_appengine\google\appengine\ext\bulkload下。根据K兄文中指示:non-ascii CSV data not handled by google.appengine.ext.bulkload (Unicode errors),此文可以解决unicode编码问题,于是我也按照K兄所说的去做。但整个说明还是太过于简单,所以有点晕。其实首先要到C:\Program Files\Google\google_appengine\google\appengine\ext\bulkload目录下将__init__.py文件拷贝出来,重命名为bulkload.py拷贝在站点根目录下,这个时候只需要import bulkload就可以调用了,而不去调用Google提供的bulkload。但对bulkload是需要作出调整的,由于没有csv.reader并不能处理中文字,所以我们需要将reader处理成支持unicode,在
return (httplib.BAD_REQUEST, ''.join(output)) 和
buffer = StringIO.StringIO(data) 之间增加一下语句
def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
dialect=dialect, **kwargs)
for row in csv_reader:
yield [unicode(cell, 'utf-8') for cell in row]
def utf_8_encoder(unicode_csv_data):
for line in unicode_csv_data:
yield line.encode('utf-8')
而下面一行的reader = csv.reader(buffer, skipinitialspace=True)也需要改成reader = unicode_csv_reader(buffer, skipinitialspace=True)。保存后就可以直接使用了,该文件是支持Unicode的。
但是经过我的使用,发现还是无法搞定中文字,无法上传,因为我用中文的上传总是失败,报500错误,而字段全部使用英文则没有问题,折腾了半天,都没有搞明白,最后准备放弃的时候,大概在395行左右,看到一句data = data.encode('utf-8')。我将其注释掉,结果上传就OK了。不解不解。中文数据的上传还真的很折腾人,不过总算能够上传了,虽然后台不能浏览数据表。
这里让人最晕的是,Datastore是可以支持中文的,并且对象列表是可以看到中文的,而再浏览对象的时候就报错了。而我通过URL进行上传到Datastore中的数据结果自动被encode了。同时进入后台如果没有中文内容的对象,将某一对象的某一字段修改为中文,结果也是可以显示和浏览察看详细信息的。够纳闷的。
#效率问题
感觉GAE提供bulkload_client.py工具也算是无奈之举,只能说有总归比没有好。效率方面,我将4640条数据(14个字段,8个unicode其余为str)上传到本地GAE,用时2分40秒,平均每秒30条数据,看上去也许还行,但我想如果上传到GAE上那就需要考虑网络速度了,此点也许还能够接受吧。而最让我无法承受的是,这800多K的数据,上传完成后python.exe占用了我200多M的内存,并且不释放,好像我必须要关闭GAE才能释放。而CPU的使用率也是吓人,双核CPU基本上爆满一个,可见还是非常占用CPU的,而对于GAE来说CPU是有限制的,不能长期高CPU。看GAE的数据上传真是个问题,CPU和时间的等待都是问题,暂时看来也只有这么凑活着了。
不知道有没有说清楚,文中的疑点也望有能力的大侠给我解惑一下。