关于解决 Python 乱码问题的终极解决方案 (TL;DR)
有个特别好玩的现象, 当我们为了 python 编码头疼的时候, 几乎搜索到所有的文章都会先发一通牢骚然后在无可奈何地写解决思路 (是解决思路不是方案) 这个问题真不是新手问题, 即使是十几年 python 老手也经常头疼中国外国都一样看看这个 python 专家在 PyCon 大会上用半个多小时讲解乱码的视频就了解了, 他自己都给自己的来回 encoding, decoding, encoding, decoding 说晕了, 台下举手他都拒绝回答, 可想而知这个问题复杂性
我认为, 几乎每个 pythoner, 都会有一段人生浪费在了编码上可以说这个问题, 是如果你不彻彻底底解决, 就永远会崩溃的地步翻看我曾经写的数篇文章就知道了:
对 Python 2.x 的通宵抱怨
Python 中文字符的理解: str()repr()print
Python 里中文编码的理解: unicodeutf-8gbk
牢骚结束, 下面是我又一次用了两个整天才测试整理书写完成的 ipython notebook 笔记 ipynb 格式的笔记源文件在这里, 当然有可能会链接失效, 有喜欢 ipython 的 live coding 笔记的且想要用这个笔记测试编码的, 请联系我
简单来说, 先要记住, 在 Python2 里字符串只有两大阵营:
unicode 和 bytes
如果 type(字符串)显示结果是 str, 那么就是指的 bytes 二进制码
而其它各种我们所说的 utf-8,gb2312 等等也都是 Unicode 的不同实现方式
这里不要去考虑那么复杂, 只要先记住这两大阵营就行
encoding 和 decoding
绝对要记住的:
从 unicode 转换到 bytes, 这个叫 encoding, 编码
从 bytes 转换到 unicode, 这个叫 decoding, 解码
来回记住这个问题, 才能进入下一步!
然后来看个案例
通过上面两种格式的对比我们看到, str 和 unicode 的各种区别
那么, 既然变量里面会出现两种不同的格式, 如果我们把两种格式的字符串连在一起操作会发生什么呢?
如下:
看! 著名的编码错误
UnicodeDecodeError: 'ascii'codec can 't decode'
就这样出现了!
以上是我们用显性字符串来比较两种格式字符串的区别
但是, 我们经常性处理 python 编码问题, 都不是在这种显性的字符串上出现的, 不是从网上爬取的就是从本地文件读取的, 意思就是文件内容庞大, 编码格式很难猜到是什么
所以这里我们将问题再拆分为两部分讨论: 本地文件和网络资源
本地文件编码测试
首先在本地建立一个有中文的以 utf-8 格式保存的文本文件(实际上无论. txt 还是. md 等都无所谓, 内容是一样的)
内容只有'你好'
然后我们来读取一下:
上面看到, 从文件读取出来的, 就是 bytes 二进制格式
那么如果要把 bytes 转化为 unicode, 就要解码, 也就是 decoding.
这种时候实际上是最迷糊也最容易造成之后错误的, 就是分不清该编码还是该解码
所以上面提到, 必须要记住这两个区别
那么如果现在我搞反了怎么办? 就会再次出现下面错误:
话说回来, 我们该怎么统一他们呢?
为了避免两种格式的字符串在一起乱搞, 统一他们是必须的但是以哪一种为统一的呢, unicode 还是 bytes?
下面我们来看看做常用的环境下字符串都是什么格式
这样就明白了: 除了 r.text 返回的内容外, 其它几乎都是使用 str 格式, 也就是 bytes 二进制码所以我们只要转化 requests 相关的内容就行!
实际上, requests 返回的 response 中, 除了用 response.text 获取内容, 我们还可以用 response.content 获取同样的内容, 只不过是 bytes 格式
那就正和我们意, 不用再去转化每一个地方的字符串, 而只要盯紧这一个地方就足够了
为什么我们不能把所有字符串变量统一为 unicode 呢?
提前说明, 变成 unicode 的过程, 叫 decoding 不要记错
因为像 response.text 经常把 ISO8859 等猜不到也检测不到编码 (机率很低) 的字符串扔过来, 如果遇到的话, 是很麻烦的
decoding 有两种方法:
unicode(b '你好)
b'你好'.decode('utf - 8')'这里因为不知道来源的编码, 所以必须用 unicode()来解码, 而不能用. decode('utf-8'), 因为显然你不能乱写解码名称, 如果来源果真是(很大几率是)ISO8859 等方式, 那么错误的解码肯定会产生乱码, 或者直接程序报错切记!
所以这里只能用 unicode()解码如下例:
本阶段总结: 一定记住, 全文都统一用 str 格式字符串
只要盯紧 requests 等相关的网络操作就好了, 只要控制好外来源的字符串, 统一为 str, 其它一切都好说!
下面是一个从获取网络资源 (含中文且被 requests 认为编码是 ISO8850 的网页) 到本地操作且存储到本地文件的完整测试
大功告成! 效果如下:
再也不用纠结检查每一个变量写一大堆嵌套转化方法了!
来源: https://juejin.im/entry/5a7bc8c0f265da4e9d221caa