脚本在通过REST接口传递Unicode时遇到问题
我在用Python脚本通过RESTful HTTP调用发送Unicode数据时遇到了问题。
我有一个脚本,它从网站X读取数据,然后通过REST接口把这些数据推送到网站Y。这两个系统都是开源的,运行在我们的服务器上。网站X使用PHP、Apache和PostgreSQL,而网站Y使用Java、Tomcat和PostgreSQL。处理这些数据的脚本目前是用Python写的。
总体来说,脚本运行得很好。不过,我们有一些国际用户,当处理名字中包含Unicode字符的用户时,就出现了问题。脚本的原始版本是把JSON数据读取到Python中,这些数据会自动转换成Unicode。我很确定在这之前一切都正常。为了输出数据,我使用了subprocess.Popen()来调用curl。这个方法对普通的ASCII字符有效,但在传输过程中,Unicode字符却出现了问题。我没有收到任何错误信息,但在查看网站B的结果时,数据的编码不再正确了。
我知道这些字段支持Unicode,因为我可以用Firefox手动构造请求,正确地将数据添加到网站B。
接下来的想法是,不使用curl,而是直接在Python中完成所有操作。我尝试将一个手动构造的Unicode字符串传递给Python的urllib来进行REST调用,但收到了来自urllib.urlopen()的错误:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 103-105: ordinal not in range(128)
有没有什么办法可以解决这个问题?我不想重写太多代码,但如果有其他更合适的脚本语言,我也很乐意听听。
这是我的Python测试脚本:
import urllib uni = u"abc_\u03a0\u03a3\u03a9" post = u"xdat%3Auser.login=unitest&" post += u"xdat%3Auser.primary_password=nauihe4r93nf83jshhd83&" post += u"xdat%3Auser.firstname=" + uni + "&" post += u"xdat%3Auser.lastname=" + uni ; url = u"http://localhost:8081/xnat/app/action/XDATRegisterUser" data = urllib.urlopen(url,post).read()
1 个回答
关于你的测试脚本,它失败的原因是你把一个unicode对象传给了 urllib.urlencode()
(这个函数是通过 urlopen()
自动调用的)。这个函数不支持unicode对象,所以它会默认用 ascii
编码来处理,这样就会出错。
处理unicode对象进行POST请求的最简单方法是明确一些;先把你的数据收集起来,构建一个字典,然后用合适的字符集对unicode值进行编码,再把这个字典进行url编码(这样就能得到一个可以POST的ascii字符串),最后发起请求。你的例子可以改写成:
import urllib
import urllib2
## Build our post data dict
data = {
'xdat:user.login' : u'unitest',
'xdat:primary_password' : u'nauihe4r93nf83jshhd83',
'xdat:firstname' : u"abc_\u03a0\u03a3\u03a9",
'xdat:lastname' : u"abc_\u03a0\u03a3\u03a9",
}
## Encode the unicode using an appropriate charset
data = dict([(key, value.encode('utf8')) for key, value in data.iteritems()])
## Urlencode it for POSTing
data = urllib.urlencode(data)
## Build a POST request, get the response
url = "http://localhost:8081/xnat/app/action/XDATRegisterUser"
request = urllib2.Request(url, data)
response = urllib2.urlopen(request)
编辑:更一般来说,当你用python发起http请求(比如 urllib2.urlopen
)时,返回的内容并不会自动解码成unicode。这意味着你需要注意发送这个内容的服务器使用的编码方式。查看一下 content-type
头部;通常它会包含 charset=xyz
。
尽早解码你的输入,尽量晚些编码你的输出,这样做是比较明智的。