urllib2、Google App Engine 与 Unicode 问题
大家好,我正在学习谷歌应用引擎,所以遇到了一些问题...
我现在的困境是这样的。我有一个数据库,
class Website(db.Model):
web_address = db.StringProperty()
company_name = db.StringProperty()
content = db.TextProperty()
div_section = db.StringProperty()
local_links = db.StringProperty()
absolute_links = db.BooleanProperty()
date_updated = db.DateTimeProperty()
我遇到的问题是关于内容属性的。
我使用 db.TextProperty() 是因为我需要存储网页的内容,而这些内容超过了500个字节。
我遇到的问题是 urllib2.readlines() 返回的是 Unicode 格式。当我把它放进 TextProperty() 时,它却转换成了 ASCII 格式。有些字符的编码值大于128,这就导致了一个 UnicodeDecodeError 的错误。
有没有简单的方法可以绕过这个问题?大部分情况下,我对那些字符并不在意...
我的错误信息是:
追踪记录(最近的调用在最前面):
文件 "/base/python_runtime/python_lib/versions/1/google/appengine/ext/webapp/init.py",第 511 行,在 call 中 handler.get(*groups) 文件 "/base/data/home/apps/game-job-finder/1.346504560470727679/main.py",第 61 行,在 get 中 x.content = website_data_joined 文件 "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/init.py",第 542 行,在 set 中 value = self.validate(value) 文件 "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/init.py",第 2407 行,在 validate 中 value = self.data_type(value) 文件 "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore_types.py",第 1006 行,在 new 中 return super(Text, cls).new(cls, arg, encoding) UnicodeDecodeError: 'ascii' 编解码器无法解码字节 0xc2 在位置 2124:序号不在范围内(128)
1 个回答
看起来从 readlines
返回的内容不是 Unicode 字符串,而是字节字符串(也就是 str
类型的实例,可能包含非 ASCII 字符)。这些字节是从 HTTP 响应体中收到的原始数据,具体表示什么字符串取决于使用的编码方式。在把它们当作文本使用之前,需要先进行“解码”(字节和字符是不同的)。
如果编码是 UTF-8,这段代码应该能正常工作:
f = urllib2.open('http://www.google.com')
website = Website()
website.content = db.Text(f.read(), encoding = 'utf-8-sig') # 'sig' deals with BOM if present
需要注意的是,实际的编码方式在不同的网站上是不同的(有时候甚至同一个网站的不同页面也会不一样)。使用的编码应该在 HTTP 响应的 Content-Type 头中包含(可以参考 这个问题了解如何获取),但如果没有,可能会在 HTML 的头部的 meta 标签中找到(这种情况下提取起来就复杂得多):
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
还有一些网站根本不指定编码,或者指定了错误的编码。
如果你真的只关心 ASCII 字符,可以直接忽略其他字符,这样就可以了:
f = urllib2.open('http://www.google.com')
website = Website()
content = unicode(f.read(), errors = 'ignore') # Ignore characters that cause errors
website.content = db.Text(content) # Don't need to specify an encoding since content is already a unicode string