使用Google App Engine上传并解析CSV文件

7 投票
3 回答
8266 浏览
提问于 2025-04-15 23:32

我在想有没有人能帮我解答一下关于Python和GAE的问题。我正在通过一个表单上传一个CSV文件到GAE的数据存储。

class CSVImport(webapp.RequestHandler):
  def post(self):
     csv_file = self.request.get('csv_import')
     fileReader = csv.reader(csv_file)
     for row in fileReader:       
       self.response.out.write(row) 

我遇到了和其他人提到的同样的问题 - http://groups.google.com/group/google-appengine/browse_thread/thread/bb2d0b1a80ca7ac2/861c8241308b9717

也就是说,csv.reader在处理时是逐个字符地读取,而不是按行读取。谷歌的一位工程师给出了这样的解释:

调用self.request.get('csv')会返回一个字符串。当你遍历一个字符串时,你是逐个字符地遍历,而不是按行遍历。你可以在这里看到这个区别:

 class ProcessUpload(webapp.RequestHandler): 
   def post(self): 
     self.response.out.write(self.request.get('csv')) 
     file = open(os.path.join(os.path.dirname(__file__), 'sample.csv')) 
     self.response.out.write(file) 

     # Iterating over a file 
     fileReader = csv.reader(file) 
     for row in fileReader: 
       self.response.out.write(row) 

     # Iterating over a string 
     fileReader = csv.reader(self.request.get('csv')) 
     for row in fileReader: 
       self.response.out.write(row) 

我真的不太明白这个解释,而且尝试实现的时候也没有成功。有没有人能提供一个更清晰的解释和解决方案呢?

谢谢,
August

3 个回答

0

你需要使用 csv_file = self.request.POST.get("csv_import") 这个方式,而不是 csv_file = self.request.get("csv_import")

第二种方法只会给你一个字符串,就像你在最开始的帖子里提到的那样。但是通过 self.request.POST.get 访问的话,你会得到一个 cgi.FieldStorage 对象。

这意味着你可以用 csv_file.filename 来获取这个对象的文件名,用 csv_file.type 来获取文件的类型。此外,如果你访问 csv_file.file,它是一个 StringO 对象(这是来自 StringIO 模块 的只读对象),而不仅仅是一个字符串。正如 ig0774 在 他的回答 中提到的,StringIO 模块让你可以把字符串当作文件来使用。

所以,你的代码可以简单写成:

class CSVImport(webapp.RequestHandler):
  def post(self):
     csv_file = self.request.POST.get('csv_import')
     fileReader = csv.reader(csv_file.file)
     for row in fileReader:
       # row is now a list containing all the column data in that row
       self.response.out.write(row)
8

我想不出比你提到的谷歌工程师更清楚的解释了。那我们来简单拆解一下。

Python的csv模块是用来处理文件类对象的,也就是文件或者像Python文件那样工作的东西。因此,csv.reader()需要一个文件对象作为它唯一的必要参数。

webapp.RequestHandler的请求对象可以访问在表单中提交的HTTP参数。在HTTP中,参数是以键值对的形式提交的,比如csv=record_one,record_two。当你调用self.request.get('csv')时,它会返回与键csv相关联的,这个值是一个Python字符串。Python字符串并不是文件类对象。显然,当csv模块不理解这个对象时,它会退而求其次,简单地逐个字符进行迭代(在Python中,字符串可以逐个字符遍历,比如for c in 'Test String': print c会把字符串中的每个字符单独打印在一行上)。

幸运的是,Python提供了一个叫做StringIO的类,可以让字符串像文件类对象一样使用。所以(假设GAE支持StringIO,而没有理由不支持),你应该可以这样做:

class ProcessUpload(webapp.RequestHandler): 
   def post(self): 
     self.response.out.write(self.request.get('csv')) 

     # Iterating over a string as a file 
     stringReader = csv.reader(StringIO.StringIO(self.request.get('csv')))
     for row in stringReader: 
        self.response.out.write(row) 

这样就能按你预期的方式工作了。

编辑 我假设你使用的是类似<textarea/>的东西来收集csv文件。如果你是上传附件,可能需要不同的处理方式(我对Python GAE或它如何处理附件不是很熟悉)。

13

简短的回答,试试这个:

fileReader = csv.reader(csv_file.split("\n"))

长一点的回答,考虑以下内容:

for thing in stuff:
  print thing.strip().split(",")

如果“stuff”是一个文件指针,那么每一项就是一行。如果“stuff”是一个列表,那么每一项就是一个项目。如果“stuff”是一个字符串,那么每一项就是一个字符。

使用csv.reader返回的对象进行遍历,得到的效果就像遍历传入的对象,只不过每一项都是经过CSV解析的。如果你遍历一个字符串,你会得到每个字符的CSV解析版本。

撰写回答