python3编码问题汇总


Posted in Python onSeptember 06, 2016

这两天写了个监测网页的爬虫,作用是跟踪一个网页的变化,但运行了一晚出现了一个问题。。。。希望大家不吝赐教!
我用的是python3,错误在对html response的decode时抛出,代码原样为:

response = urllib.urlopen(dsturl)
content = response.read().decode('utf-8')

抛出错误为

File "./unxingCrawler_p3.py", line 50, in getNewPhones
  content = response.read().decode()
UnicodeDecodeError: 'utf8' codec can't decode byte 0xb2 in position 24137: invalid start byte

之前运行都没问题,经过一晚上就出现了。。。。最不明白的是在它声明为utf-8编码的网页中为什么会出现utf-8无法解析的字符?

后来经过热心网友的提醒,才发现需要使用decode('utf-8', 'ignore')

为了彻底闹明白python的编码问题,特分享下文,希望对大家熟悉python的编码问题带来些帮助

1.从字节说起:

一个字节包括八个比特位,每个比特位表示0或1,一个字节即可表示从00000000到11111111共2^8=256个数字。一个ASCII编码使用一个字节(除去字节的最高位作为作奇偶校验位),ASCII编码实际使用一个字节中的7个比特位来表示字符,共可表示2^7=128个字符。比如ASCII编码中的01000001(即十进制的65)表示字符'A',01000001加上32之后的01100001(即十进制的97)表示字符'a'。现在打开Python,调用chr和ord函数,我们可以看到Python为我们对ASCII编码进行了转换。如图

python3编码问题汇总

第一个00000000表示空字符,因此ASCII编码实际上只包括了 字母、标点符号、特殊符号等共127个字符。因为ASCII是在美国出生的,对于由字母组成单词进而用单词表达的英文来说也是够了。但是中国人、日本人、 韩国人等其他语言的人不服了。中文是一个字一个字,ASCII编码用上了浑身解数256个字符都不够用。

因此后来出现了Unicode编码。Unicode编码通常由两个字节组成,共表示256*256个字符,即所谓的UCS-2。某些偏僻字还会用到四个字节,即所谓的UCS-4。也就是说Unicode标准也还在发展。但UCS-4出现的比较少,我们先记住: 最原始的ASCII编码使用一个字节编码,但由于语言差异字符众多,人们用上了两个字节,出现了统一的、囊括多国语言的Unicode编码。

在Unicode中,原本ASCII中的127个字符只需在前面补一个全零的字节即可,比如前文谈到的字符‘a':01100001,在Unicode中变成了00000000 01100001。不久,美国人不开心了,吃上了世界民族之林的大锅饭,原本只需一个字节就能传输的英文现在变成两个字节,非常浪费存储空间和传输速度。

人们再发挥聪明才智,于是出现了UTF-8编码。因为针对的是空间浪费问题,因此这种 UTF-8编码是可变长短的 ,从英文字母的一个字节,到中文的通常的三个字节,再到某些生僻字的六个字节。解决了空间问题,UTF-8编码还有一个神奇的附加功能,那就是兼容了老大哥的ASCII编码。一些老古董软件现在在UTF-8编码中可以继续工作。

注意除了英文字母相同,汉字在Unicode编码和UTF-8编码中通常是不同的。比如​汉字的‘中'字在Unicode中是01001110 00101101,而在UTF-8编码中是11100100 10111000 10101101。

我们祖国母亲自然也有自己的一套标准。那就是GB2312和GBK。当然现在挺少看到。通常都是直接使用UTF-8。

2.Python3中的默认编码

Python3中默认是UTF-8,我们通过以下代码:

import sys

sys.getdefaultencoding()

可查看Python3的默认编码。​

python3编码问题汇总

3.Python3中的​encode和decode

Python3中字符编码经常会使用到decode和encode函数。特别是在抓取网页中,这两个函数用的熟练非常有好处。encode的作用,使我们看到的直观的字符转换成计算机内的字节形式。decode刚好相反,把字节形式的字符转换成我们看的懂的、直观的、“人模人样”的形式。

python3编码问题汇总

\x表示后面是十六进制, \xe4\xb8\xad即是二进制的 11100100 10111000 10101101。也就是说汉字‘中'encode成字节形式,是 11100100 10111000 10101101。同理,我们拿 11100100 10111000 10101101也就是 \xe4\xb8\xad来decode回来,就是汉字‘中'。完整的应该是 b'\xe4\xb8\xad',在Python3中, 以字节形式表示的字符串则必须加上 前缀b,也就是写成上文的b'xxxx'形式。

前文说的Python3的默认编码是UTF-8,所以我们可以看到,Python处理这些字符的时候是以UTF-8来处理的。因此从上图可以看到,就算我们通过encode('utf-8')特意把字符encode为UTF-8编码,出来的结果还是相同:b'\xe4\xb8\xad'。

明白了这一点,同时我们知道​UTF-8兼容ASCII,我们可以猜想大学时经常背诵的‘A'对应ASCII中的65,在这里是不是也能正确的decode出来呢。十进制的65转换成十六进制是41,我们尝试下:

b'\x41'.decode()

结果如下。果然是字符‘A'

python3编码问题汇总

4.Python3中的​编码转换

据说字符在计算机的内存中统一是以Unicode编码的。只有在字符要被写进文件、存进硬盘或者从服务器发送至客户端(例如网页前端的代码)时会变成utf-8。但其实我比较关心怎么把这些字符以Unicode的字节形式表现出来,露出它在内存中的庐山正面目的。这里有个照妖镜:

xxxx.encode/decode('unicode-escape')

python3编码问题汇总

b'\\u4e2d'还是b'\u4e2d,一个斜杠貌似没影响。同时可以 发现在shell窗口中,直接输 '\u4e2d'和输入b '\u4e2d'.decode('unicode-escape')是相同的,都会打印出汉字‘中', 反而是 '\u4e2d'.decode('unicode-escape')会报错。说明 说明Python3不仅支持Unicode,而且一个‘\uxxxx'格式的 Unicode字符 可被辨识且被等价于str类型。

python3编码问题汇总

如果我们知道一个Unicode字节码,怎么变成UTF-8的字节码呢。懂了以上这些,现在我们就有思路了,先decode,再encode。代码如下:

​xxx.decode('unicode-escape').encode()

python3编码问题汇总

​最后的扩展

还记得刚刚那个ord吗。时代变迁,老大哥ASCII被人合并,但ord还是有用武之地。试试ord('中'),输出结果是20013。20013是什么呢,我们再试试hex(ord('中')),输出结果是'0x4e2d',也就是20013是我们在上文见面了无数次的x4e2d的十进制值。这里说下hex,是用来转换成十六进制的函数,学过单片机的人对hex肯定不会陌生。

最后的扩展,在网上看到的他人的问题。我们写下类似于'\u4e2d'的字符,Python3知道我们想表达什么。但是让Python读取某个文件的时候出现了'\u4e2d',是不是计算机就不认识它了呢?后来下文有人给出了答案。如下:

import codecs

file = codecs.open( "a.txt", "r", "unicode-escape" )

u = file.read()

print(u)
Python 相关文章推荐
python聊天程序实例代码分享
Nov 18 Python
python实现多线程采集的2个代码例子
Jul 07 Python
Python随机生成数据后插入到PostgreSQL
Jul 28 Python
Python基于回溯法子集树模板解决马踏棋盘问题示例
Sep 11 Python
Python程序员面试题 你必须提前准备!
Jan 16 Python
浅析python实现scrapy定时执行爬虫
Mar 04 Python
Python实现的绘制三维双螺旋线图形功能示例
Jun 23 Python
python实现AES加密与解密
Mar 28 Python
通过实例了解python property属性
Nov 01 Python
keras 解决加载lstm+crf模型出错的问题
Jun 10 Python
python实现测试工具(二)——简单的ui测试工具
Oct 19 Python
浅谈Python数学建模之固定费用问题
Jun 23 Python
用Python实现命令行闹钟脚本实例
Sep 05 #Python
Python爬虫爬取美剧网站的实现代码
Sep 03 #Python
Python选课系统开发程序
Sep 02 #Python
简单谈谈Python中函数的可变参数
Sep 02 #Python
Python实现自动添加脚本头信息的示例代码
Sep 02 #Python
利用Python获取操作系统信息实例
Sep 02 #Python
好用的Python编辑器WingIDE的使用经验总结
Aug 31 #Python
You might like
php下通过伪造http头破解防盗链的代码
2010/07/03 PHP
destoon实现调用图文新闻的方法
2014/08/21 PHP
PHP加密解密类实例分析
2015/04/20 PHP
PHP PDOStatement::rowCount讲解
2019/02/01 PHP
Nigma vs AM BO3 第一场2.13
2021/03/10 DOTA
原生Js与jquery的多组处理, 仅展开一个区块的折叠效果
2011/01/09 Javascript
使用JavaScript构建JSON格式字符串实现步骤
2013/03/22 Javascript
JS获取几种URL地址的方法小结
2014/02/26 Javascript
Javascript 拖拽雏形(逐行分析代码,让你轻松了拖拽的原理)
2015/01/23 Javascript
JS实现带关闭功能的阿里妈妈网站顶部滑出banner工具条代码
2015/09/17 Javascript
AngularJS基础 ng-href 指令用法
2016/08/01 Javascript
值得分享的JavaScript实现图片轮播组件
2016/11/21 Javascript
thinkphp标签实现bootsrtap轮播carousel实例代码
2017/02/19 Javascript
AngularJS全局警告框实现方法示例
2017/05/18 Javascript
Angularjs 实现动态添加控件功能
2017/05/25 Javascript
AngularJS中重新加载当前路由页面的方法
2018/03/09 Javascript
Vue实现自定义下拉菜单功能
2018/07/16 Javascript
vue实现双向绑定和依赖收集遇到的坑
2018/11/29 Javascript
微信公众号获取用户地理位置并列出附近的门店的示例代码
2019/07/25 Javascript
vue 清空input标签 中file的值操作
2020/07/21 Javascript
JS实现鼠标按下拖拽效果
2020/07/23 Javascript
[46:44]VG vs TNC Supermajor小组赛B组败者组决赛 BO3 第一场 6.2
2018/06/03 DOTA
html5 touch事件实现触屏页面上下滑动(一)
2016/03/10 HTML / CSS
canvas实现圆绘制的示例代码
2019/09/11 HTML / CSS
HTML5中在title标题标签里设置小图标的方法
2020/06/23 HTML / CSS
德国运动营养和健身网上商店:Myprotein.de
2018/07/18 全球购物
C/C++有关内存的思考题
2015/12/04 面试题
物业保安主管岗位职责
2013/12/25 职场文书
交通安全寄语大全
2014/04/08 职场文书
亲属关系公证书
2014/04/08 职场文书
环保倡议书格式范文
2014/05/14 职场文书
奥巴马当选演讲稿
2014/09/10 职场文书
秋季校运会广播稿100字
2014/09/18 职场文书
2014年学校德育工作总结
2014/12/05 职场文书
Django分页器的用法你都了解吗
2021/05/26 Python
世界十大评分最高的动漫,CLANNAD上榜,第八赚足人们眼泪
2022/03/18 日漫