• 将中文csv数据上传到GAE Datastore(Bulk Data Uploader工具)


    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和时间的等待都是问题,暂时看来也只有这么凑活着了。

    不知道有没有说清楚,文中的疑点也望有能力的大侠给我解惑一下。

  • 相关阅读:
    徒手画个disk不容易啊。。。
    fast powf
    SSE sqrt还是比C math库的sqrtf快了不少
    Mongoose也是个大坑
    A tiny program to benchmark image transpose algorithms
    On extracting ops from LLVM backend
    Into concurrent LRU caching once again
    性能大坑
    多项式在线拟合神器
    Spark 1.6.1源码编译
  • 原文地址:https://www.cnblogs.com/Tangf/p/1406203.html
Copyright © 2020-2023  润新知