本文来自这个项目引出的问题。该项目是一个vim插件,可以利用有道词典翻译英文单词。挺棒的一个项目,不过代码中对url进行编码的部分可能存在一些问题,已提交Issue,这里进行记录备份与细节补充。

文中用py2指代python2.7,用py3指代python3.4。


1. 问题描述

对于url的编码,应该进行percent-encoding,而不是str.encode()

py3中应当用urllib.parse.quote()进行percent-encoding,py2中对应的方法是urllib.pathname2url()


2. 问题定位

ydt.vim的第53行


3. 示例

原项目中,主要通过

url = 'http://dict.youdao.com/fsearch?q=' + word.encode('utf-8')

来获取翻译内容。下面以word='hello'word='你好'为例,分别进行说明。

  • py2+英文(结果相同,不影响使用):
>>> 'hello'.encode('utf-8')
'hello'
>>>
>>> urllib.pathname2url('hello')
'hello'
  • py2+中文(结果不同):
>>> '你好'.encode('utf-8')
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
>>>
>>> urllib.pathname2url('你好')	
'%E4%BD%A0%E5%A5%BD'

可以看到对中文用str.encode('utf-8')会直接报错,换成gbk, gbk2312也是一样。

  • py3+英文(结果不同)
>>> 'hello'.encode('utf-8')
b'hello'
>>>
>>> urllib.parse.quote('hello')
'hello'

注意到str.encode()之后的结果中多了一个b,根据这里

如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

这时如果再用

url = 'http://dict.youdao.com/fsearch?q=' + word.encode('utf-8')

也会报错,因为strbytes类型不能直接拼接。

  • py3+中文(结果不同)
>>> '你好'.encode('utf-8')
b'\xe4\xbd\xa0\xe5\xa5\xbd'
>>>
>>> urllib.parse.quote('你好')
'%E4%BD%A0%E5%A5%BD'

不能用str.encode(),原因同上。另外,同样根据这里

在bytes中,无法显示为ASCII字符的字节,用 \x## 显示。


4. 结论

如果使用str.encode(),把不同结果组合成url用于获取资源

url = 'http://dict.youdao.com/fsearch?q=' + word.encode('utf-8')

除了py2+英文的组合无影响,其他三者都不是期望结果。

因此:

  • 如果使用str.encode(),py2英文无影响,中文有影响。py3中英文都有影响。
  • 应使用percent-encoding
  • 由于是作为vim插件,环境是py2+英文(一般不会出现中文),所以无影响。此条Issue仅作为建议。


综上可以看出py3对数据类型和编码的控制更为精细与明确。所以在使用urlopen()时,如果url中包含了未经编码的中文,是无法正确获取资源的。

另外,我们可以在浏览器地址栏直接输入中文进行搜索,这是因为浏览器帮我们完成了编码和转换的过程。比如你先输入中文进行搜索,然后再把url复制下来,会发现中文变成了很多%和英文组成的字符串。

最后,因为原来的有道api不知道啥情况不能用了,感谢vim-youdao-translater项目提供了一种获取资源的新思路,我在GitHub上传了不需要key和keyfrom的新版本查词器,代码也精简了不少。


(END)


相关阅读