我是一个python小白,最近在模拟微信的web协议,无聊练练,然后碰到中文乱码的问题,看文档问谷歌,弄了一天了也无果,所以请教大家。
我借助的是requests来作get、post请求,创建了一个session全程使用,我这次请求返回的是一个json,
当我打印原文(response.text)的时候中文显示如下:
36æ°ª(36Kr.com)æ¯ä¸å½é¢å
çç§ææ°åªä½ï¼æ们æ¥éææ°çäºèç½ç§ææ°é»ä»¥åæææ½åçäºèç½åä¸ä¼ä¸ã
进行json解析后(response.json())貌似已经变成unicode编码的ascii,打印的结果中文是这样的:
u'36\xe6\xb0\xaa(36Kr.com)\xe6\x98\xaf\xe4\xb8\xad\xe5\x9b\xbd\xe9\xa2\x86\xe5\x85\x88\xe7\x9a\x84\xe7\xa7\x91\xe6\x8a\x80\xe6\x96\xb0\xe5\xaa\x92\xe4\xbd\x93\xef\xbc\x8c\xe6\x88\x91\xe4\xbb\xac\xe6\x8a\xa5\xe9\x81\x93\xe6\x9c\x80\xe6\x96\xb0\xe7\x9a\x84\xe4\xba\x92\xe8\x81\x94\xe7\xbd\x91\xe7\xa7\x91\xe6\x8a\x80\xe6\x96\xb0\xe9\x97\xbb\xe4\xbb\xa5\xe5\x8f\x8a\xe6\x9c\x80\xe6\x9c\x89\xe6\xbd\x9c\xe5\x8a\x9b\xe7\x9a\x84\xe4\xba\x92\xe8\x81\x94\xe7\xbd\x91\xe5\x88\x9b\xe4\xb8\x9a\xe4\xbc\x81\xe4\xb8\x9a\xe3\x80\x82'
json解析后是一个dict,当我单独打印键值的时候中文显示是:
36æ°ª(36Kr.com)æ¯ä¸å½é¢å
çç§ææ°åªä½ï¼æ们æ¥éææ°çäºèç½ç§ææ°é»ä»¥åæææ½åçäºèç½åä¸ä¼ä¸ã
{'connection': 'close', 'content-type': 'text/plain', 'content-length': '14315', 'content-encoding': 'deflate'}
我该如何正确的打印这个中文内容或者该如何与已知中文字符串比较,我的环境是
为了解决这个问题,我特地看了微信的web前端js关于那一段的请求处理,表示水平有限没发现什么。
请大家帮忙解释一下上述原因,或者给个处理中文的思路!!
1
lincanbin 2015-02-23 12:14:39 +08:00 4
print userinfo['NickName'].encode('iso8859-1')
用PHP之类的语言的话就不需要考虑编码问题,人生苦短,不要用Python。 |
2
fish267 2015-02-23 12:18:46 +08:00 1
方法一: python3 解除编码烦恼。
方法二: def unicode_init(obj, encoding = 'utf-8'): if isinstance(obj, basestring): if not isinstance(obj, unicode): obj = unicode(obj, encodeing) return obj |
3
zwzmzd 2015-02-23 12:24:02 +08:00 via Android 1
dict里面的中文用unicode形式存储,传输之前用json.dumps导出标准json,各个套件跟着规范走不会有问题
|
5
funagi 2015-02-23 12:32:52 +08:00 1
试试
``` response = session.get('http://xxxx') print(response.encoding) response.encoding = 'utf-8' print(response.json()) ``` |
9
Sylv 2015-02-23 13:00:29 +08:00 via iPhone 3
这种情况是因为数据源的编码声明不规范,导致 reuqests 无法判断出返回数据的正确编码,从而使用了它的默认编码 iso8859-1 来将数据转换为 unicode,没使用正确的编码 utf-8,于是就出现乱码了。
解决办法一就是手动设置返回数据的正确编码:response.encoding = 'utf-8'。这样结果就是正确的 unicode 类型字符串。 或者将错误的 unicode 用同样的 iso8859-1 编码转换回去原本的 utf-8 编码的 str 类型字符串:data.encode('iso8859-1')。 |
10
a2z 2015-02-23 13:02:30 +08:00 1
前几天刚入这个坑:
Python内部数据结构标准内码是unicode json.loads, json.dumps之类的只能接受unicode字符串,有些没有特殊字符的utf8之类可以侥幸通过 requests.text会自动把网页解码unicode,输入到json模块的时候不用.encode(),那样画蛇添足 你显示,保存的时候再用encode转换成其他格式比如utf8 |
11
billlee 2015-02-23 13:13:26 +08:00 1
```python
data_ugly = response.json() Signature = data_ugly['Signature'].encode('iso8859-1').decode('utf-8')` ``` 估计换 python 3 是没用的,可以查查响应头的 Content-Type 里的 charset 是不是 utf-8. 如果不是,那就是你访问的网站没有处理好编码问题;如果是,那就是 requests 对编码的处理 有问题。 |
12
popbones 2015-02-23 13:31:49 +08:00
学python必经之路
|
14
wozhapen OP @a2z 你的解释可能是对的,但是我有试过打印的时候encode('utf-8'),不行的。。
|
15
wozhapen OP @billlee 恩恩,你的解决方式跟一楼类似。
碰到这个问题,我第一时间就是看头部,我在描述里也贴出了是'content-type': 'text/plain',编码未知,然后我就不知道该怎么处理了。。 |
16
codegear 2015-02-23 14:30:11 +08:00
Python中实际上有两种字符串,分别是str类型和unicode类型,这两者都是basestring的派生类。str实际上相当于string, 而unicode则是标准的字符串,相当于wstring。
其中I/O读入的基本都是str类型,也就是说Python不关心这些字节代表什么含义。但是如果需要做真正的字符串操作的时候,建议使用unicode类型,这样确保不存在编码问题。 JSON理论上应该可用ASCII完全表示,其中的宽字符用类似\u1111的形式表达。但是现在有很多不规范的JSON出现,例如s="""{"标题": "Hello, world"}"""。此时就需要你自己确定这部分内容用字节表达时,究竟用了什么编码,如果是UTF-8,那么就首先s_u = s.decode("UTF-8")变成unicode字符串。 另一方面,只要保证这段字符串的编码和Python运行时环境的编码保持一致,也是可以直接loads(str类型变量)的。例如Linux下如果LANG是UTF-8,那么python屁颠屁颠跑着的时候就可以直接把UTF-8编码的JSON串塞进去。 |
17
codegear 2015-02-23 14:31:29 +08:00
貌似偏题了,sorry,请无视
|
18
jas0ndyq 2015-02-23 15:11:48 +08:00 via iPad
python编码着实坑
|
19
mhycy 2015-02-23 18:16:35 +08:00
不要尝试在交互式解析器或者命行环境中直接输出文本,一个比一个坑
最可靠的做法是把文本保存成一个文件然后再查看。 如果进行比较,先把输入的字节码转成UTF8(解析成UTF8)字符串,然后再进行对比 |
20
wozhapen OP @mhycy 谢谢指导,我昨天试过保存在文本中也是乱码。
我试过把如下unicode编码的字节码 ``` u'36\xe6\xb0\xaa(36Kr.com)\xe6\x98\xaf\xe4\xb8\xad\xe5\x9b\xbd\xe9\xa2\x86\xe5\x85\x88\xe7\x9a\x84\xe7\xa7\x91\xe6\x8a\x80\xe6\x96\xb0\xe5\xaa\x92\xe4\xbd\x93\xef\xbc\x8c\xe6\x88\x91\xe4\xbb\xac\xe6\x8a\xa5\xe9\x81\x93\xe6\x9c\x80\xe6\x96\xb0\xe7\x9a\x84\xe4\xba\x92\xe8\x81\x94\xe7\xbd\x91\xe7\xa7\x91\xe6\x8a\x80\xe6\x96\xb0\xe9\x97\xbb\xe4\xbb\xa5\xe5\x8f\x8a\xe6\x9c\x80\xe6\x9c\x89\xe6\xbd\x9c\xe5\x8a\x9b\xe7\x9a\x84\xe4\xba\x92\xe8\x81\x94\xe7\xbd\x91\xe5\x88\x9b\xe4\xb8\x9a\xe4\xbc\x81\xe4\xb8\x9a\xe3\x80\x82' ``` 转成utf-8得到如下不想要的结果(貌似是简单的把单字节加上‘\xc2’变成双字节) ``` '36\xc3\xa6\xc2\xb0\xc2\xaa(36Kr.com)\xc3\xa6\xc2\x98\xc2\xaf\xc3\xa4\xc2\xb8\xc2\xad\xc3\xa5\xc2\x9b\xc2\xbd\xc3\xa9\xc2\xa2\xc2\x86\xc3\xa5\xc2\x85\xc2\x88\xc3\xa7\xc2\x9a\xc2\x84\xc3\xa7\xc2\xa7\xc2\x91\xc3\xa6\xc2\x8a\xc2\x80\xc3\xa6\xc2\x96\xc2\xb0\xc3\xa5\xc2\xaa\xc2\x92\xc3\xa4\xc2\xbd\xc2\x93\xc3\xaf\xc2\xbc\xc2\x8c\xc3\xa6\xc2\x88\xc2\x91\xc3\xa4\xc2\xbb\xc2\xac\xc3\xa6\xc2\x8a\xc2\xa5\xc3\xa9\xc2\x81\xc2\x93\xc3\xa6\xc2\x9c\xc2\x80\xc3\xa6\xc2\x96\xc2\xb0\xc3\xa7\xc2\x9a\xc2\x84\xc3\xa4\xc2\xba\xc2\x92\xc3\xa8\xc2\x81\xc2\x94\xc3\xa7\xc2\xbd\xc2\x91\xc3\xa7\xc2\xa7\xc2\x91\xc3\xa6\xc2\x8a\xc2\x80\xc3\xa6\xc2\x96\xc2\xb0\xc3\xa9\xc2\x97\xc2\xbb\xc3\xa4\xc2\xbb\xc2\xa5\xc3\xa5\xc2\x8f\xc2\x8a\xc3\xa6\xc2\x9c\xc2\x80\xc3\xa6\xc2\x9c\xc2\x89\xc3\xa6\xc2\xbd\xc2\x9c\xc3\xa5\xc2\x8a\xc2\x9b\xc3\xa7\xc2\x9a\xc2\x84\xc3\xa4\xc2\xba\xc2\x92\xc3\xa8\xc2\x81\xc2\x94\xc3\xa7\xc2\xbd\xc2\x91\xc3\xa5\xc2\x88\xc2\x9b\xc3\xa4\xc2\xb8\xc2\x9a\xc3\xa4\xc2\xbc\xc2\x81\xc3\xa4\xc2\xb8\xc2\x9a\xc3\xa3\xc2\x80\xc2\x82' ``` ******** 当然,对于与已知字符串比较的话,实际上可以构造出已知字符串的unicode编码的字节码,然后进行比较,其实我想知道的是更人性化的比较方式。 谢谢你的回答。 |
21
mhycy 2015-02-23 20:55:32 +08:00 1
@wozhapen
我一般做法都是decode后再encode,对于显示成16进制的编码字串,可能还需要多次转换成真正的二进制内码 第一次decode是原始字节码以一个特定字符集解析,然后变成内部unicode编码 第二次encode是内部unicode编码变成最终对比时候的一个确定的字符集与编码以保证对比时候的准确性。 对于内部写死在源码的字符串,一般直接encode成目标字符集进行对比(即便字符集一致)以保证跨平台时候的结果准确性。。 以上是我的个人做法,欢迎拍砖 |
22
aaaa007cn 2015-02-23 20:58:24 +08:00
因为这个已经是 utf8 编码的字节码
所以你应该通过 decode 拿到原始 unicode 字符串 b'36\xe6\xb0\xaa(36Kr.com)\xe6\x98\xaf\xe4\xb8\xad\xe5\x9b\xbd\xe9\xa2\x86\xe5\x85\x88\xe7\x9a\x84\xe7\xa7\x91\xe6\x8a\x80\xe6\x96\xb0\xe5\xaa\x92\xe4\xbd\x93\xef\xbc\x8c\xe6\x88\x91\xe4\xbb\xac\xe6\x8a\xa5\xe9\x81\x93\xe6\x9c\x80\xe6\x96\xb0\xe7\x9a\x84\xe4\xba\x92\xe8\x81\x94\xe7\xbd\x91\xe7\xa7\x91\xe6\x8a\x80\xe6\x96\xb0\xe9\x97\xbb\xe4\xbb\xa5\xe5\x8f\x8a\xe6\x9c\x80\xe6\x9c\x89\xe6\xbd\x9c\xe5\x8a\x9b\xe7\x9a\x84\xe4\xba\x92\xe8\x81\x94\xe7\xbd\x91\xe5\x88\x9b\xe4\xb8\x9a\xe4\xbc\x81\xe4\xb8\x9a\xe3\x80\x82'.decode('utf8') 注意开头那个 b 2.x 可省略 源编码未知的情况下 建议直接二进制写入文本文件 然后使用文本编辑器确认是哪种编码 |
23
wozhapen OP |
24
mhycy 2015-02-23 21:40:15 +08:00
@wozhapen 提起9楼的信息我倒是想起来了。。。
requests返回的数据建议手工使用chardet判定编码后解码,原因就是可能会有服务器返回不正确的编码声明 |
25
wozhapen OP @mhycy 是的,返回的头部里'content-type': 'text/plain',未有编码信息。
昨天谷歌的时候有看到chardet,倒是没试过用它来检测。这次掉坑算是长了心眼。。 |
28
zeroday 2015-02-23 22:55:02 +08:00
不知道下面的用法可不可行。
将 utf-8 转化为 unicode response = unicode( response, 'utf-8' ) 将 unicode 转化为 utf-8 输出 print response.encode('utf-8') |
29
wozhapen OP @aaaa007cn 你好,我不知道如何弄你说的那个 decode 拿到原始 unicode 字符串,所以没试,而对于你所说的接二进制写入文本文件我之前试过,
``` f = open('./html','wb') f.write(response.text) f.close() ``` 还是需要设置sys.setdefaultencoding('utf8'),不然会出现 ``` UnicodeEncodeError: 'ascii' codec can't encode characters in position 131-148: ordinal not in range(128) ``` @mhycy 我试了那个chardet,确实很强大,还给出了置信度,谢谢你,算是又多了一个工具。 {'confidence': 0.99, 'encoding': 'utf-8'} @zeroday 我试了,貌似不行。 ``` t = unicode(response.text, 'utf-8') print t.encode('utf-8') ``` 出现 TypeError: decoding Unicode is not supported *** 好了,这个帖子应该可以结了。后来者翻到的话,可以参考1、5、9楼。谢谢大家!! |
31
aaaa007cn 2015-02-24 22:18:54 +08:00
@wozhapen
那是对 20 楼的回复 >> 我试过把如下unicode编码的字节码 >> 转成utf-8得到如下不想要的结果 你给的其实是 utf8 编码的字节码 而不是 unicode 字符串 把一个 utf8 编码的字节码当作 unicode 字符串再次进行 utf8 编码当然得不到你所期望的结果 至于把网页内容直接写入文本 看源码 Response.text https://github.com/kennethreitz/requests/blob/master/requests/models.py#L740-L751 Content of the response, in unicode. Response.content https://github.com/kennethreitz/requests/blob/master/requests/models.py#L716-L718 Content of the response, in bytes. 所以你得 f = open('./html','wb') f.write(response.content) f.close() 或者显式地 f = open('./html','wb') f.write(response.text.encode('??')) # 这个该填什么编码由具体情况决定 f.close() 我个人不建议折腾 sys.setdefaultencoding @mhycy requests 自己就是用 chardet 来自动检测的 https://github.com/kennethreitz/requests/tree/master/requests/packages/chardet https://github.com/kennethreitz/requests/blob/master/requests/models.py#L636-L639 |
32
Sylv 2015-02-25 03:49:32 +08:00 1
@wozhapen 你写文件出现 UnicodeEncodeError 的原因是:
response.text 是 unicode 类型的字符串,Python 需要将其转换为 str 类型的字符串后才能写入文件,而 Python2 的默认编码是 'ascii',因此写入时其实等同执行了:f.write(response.text.encode('ascii')),而中文是无法用 ascii 来编码的,所以就出现了 UnicodeEncodeError。 虽然可以用 sys.setdefaultencoding('utf8') 来将 Python 默认编码更改为 utf-8 来解决这个问题,但这个方法从来不是解决 Python2 编码相关问题的正确方法,是有后遗症的,原因见 /t/163786 。 正确的方法是写入文件时显式地将 unicode 字符串用正确编码转换为 str 字符串:f.write(response.text.encode('utf-8'))。这样就不会出错了。 |
33
a2z 2015-02-25 09:07:22 +08:00 1
|
34
wozhapen OP @mhycy
@aaaa007cn 是的,chardet的检测确实有问题,但又好像是requests的问题,我 print response.encoding 得到是 ‘ISO-8859-1’(好像真像9楼Sylv说的那样) 而用 chardet.detect(response.text) 得到是 ‘utf-8’ 但看了源码requests 确实是用 chardet 来检测得到encoding的值,按理说应该是一样的值不。 真心谢谢 @aaaa007cn 的提醒,让我看了下源码,又get了些知识。 *** @Sylv 谢谢你的总结,分析好透彻,读了两遍,已打印下来再读,大牛啊!!方法2是个好习惯,像我这样的入门小白应该培养。 @a2z 不好意思,因为时间问题,你的资料我明天再读,有想法再请教。。 |
35
aaaa007cn 2015-02-25 23:23:00 +08:00 2
@wozhapen
同样可以通过读源码来理解 response.encoding 在这里赋值 https://github.com/kennethreitz/requests/blob/master/requests/adapters.py#L214 get_encoding_from_headers 的定义在这里 https://github.com/kennethreitz/requests/blob/master/requests/utils.py#L308-L325 所以 response.encoding 为 'ISO-8859-1' 当且仅当 response.encoding 为 None 时才会 fallback 去使用 chardet 自动检测 response.content https://github.com/kennethreitz/requests/blob/master/requests/models.py#L760-L762 也不会把检测结果保存到 response.encoding |
36
wozhapen OP @a2z 谢谢,仔细的读了那篇文章,通篇得出python的一个使用好习惯来避免编码问题,就是 @Sylv 总结的第2个解决方法
> make your code into a Unicode-only clean room > put ‘airlocks’ at the entry points to your code which will ensure that any byte sequence attempting to enter your code is properly clothed in a protective Unicode bunny suit before being allowed inside. > 将输入的 bytes 马上解码成 unicode,而在程序内部中均使用 unicode,而当在进行输出的时候,尽早将之编码成 bytes @aaaa007cn 大牛厉害啊!! 我确实该花些时间来对着文档阅读一下requests的源码。 谢谢你们!!还未入门的小白碰上你们这群大牛真幸运!!送出感谢!! |
38
Tink 2015-03-20 19:27:44 +08:00
反正我都是不管啥格式先decode成unicode,所有的处理都在unicode下进行,最后需要输出或者打印的时候,再转成utf8
|