Python
首发于Python

爬虫里面的字符串编码的坑

初学Python写爬虫程序,上手很快,但字符串的编码问题却一直困扰着我,我相信每一个学习爬虫的人都有过和我一样的困惑。一旦走上了编程之路,如果你不把编码问题搞清楚,那么它就像幽灵一般纠缠你整个职业生涯,所以,今天就谈谈Python的字符串编码。


0.前言

大家都知道,我们的计算机只能处理数字,而计算机是美国人发明的。他们只有26个字母,所有一个子集,255个字符肯定够用了,也就是用Ascii编码。但中文不一样,255个字符根本不够用,必须要2个字节才行。

对于汉字,中国有自己的一套编码规则叫gb2312,那么其他的国家,也会出一套自己的编码规则。这样就会有几百种编码方式,怎么办呢?

于是就有一套叫Unicode万国码,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

Unicode采用的做法就是增加字节数,英语要1个字节,汉语要2个字节,其他语言可能也要2个字节。怎么统一呢?于是全面都有两个字节来编码一个字符。这样虽然解决了统一的问题,但是会带来一个开销问题。

如果数据量很大的时候,明明英文只要一个字节,但因为用了Unicode硬给我分配了2个字节的空间,存储开销变大了,传输开销也变大了。

于是utf-8出现了,它是一种可变的编码方式,是一种正对Unicode的编码方式,也就是说如果你想转为utf-8,你的字符必须是Unicode格式。

1.字符与字节

一个字符不等价于一个字节,字符是人类能够识别的符号,而这些符号要保存到计算机的储存中就需要用计算机能够识别的字节来表示。一个字符往往有多种表示方法,不同的表示方法会使用不同的字节数。这里说的不同的表示方法就是指字符编码。

字符编码的作用是将人类可识别的字符转换为机器可识别的字节码,以及反向过程。例如,UNICODE才是真正的字符串,而用ASCII、UTF-8、GBK等字符编码表示的是字节串

我们写代码是写在文件中,而字符是以字节形式保存在文件中的,因此当我们在文件中定义字符串时被当作字节串也是可以理解的。

但是,我们需要的是字符串,而不是字节串(我们写代码处理的是我们能想象的数据,也就是字符串,应该不会有人想象字节串吧)。

一个优秀的编程语言,应该严格区分两者的关系并提供巧妙的完美支持。Python2在这方面就表现的很不友好。

直到08年,Python创始人龟叔越来越觉得,Python里面的好多东西已发展的不像他的初衷那样,开始变得臃肿、不简洁、并且有些设计让人摸不着头脑,比如Unicode与str类型,str与bytes类型的关系,这给很多Python程序员造成了困扰。

于是来了个大变革,Python3横空出世,不兼容Python2,Python3比Python2做了非常多的改进,其中一个就是终于把字符串变成了unicode,文件默认编码变成了utf-8,这意味着,只要用Python3,无论你的程序是以哪种编码开发的,都可以在全球各国电脑上正常显示,简直太棒啦!

2.编码与解码

Python官方文档中对Unicode字符串、字节串与编码之间的关系有这样一段描述:


Unicode字符串是一个代码点序列,代码点取值范围为0到0x10FFFF(对应十进制为1114111)。这个代码点序列在存储(包括内存和物理磁盘)中需要被表示为一组字节(0到255之间的值)。而将Unicode字符串转换为字节序列的规则称为编码。

这里说的编码不是指定字符编码,而是指编码的过程以及这个过程中所使用到的Unicode字符的代码点与字节的映射规则。这个映射不必是简单的一对一映射,因此编码过程也不必处理每个可能的Unicode字符,例如:

将Unicode字符串转换为ASCII编码的规则很简单——对于每个代码点:

  • 如果代码点数值<128,则每个字节与代码点的值相同
  • 如果代码点数值>=128,则Unicode字符无法在此编码中进行表示(这种情况下,Python会引发一个UnicodeEncodeError异常)

将Unicode字符串转换为utf-8编码使用以下规则:

  • 如果代码点数值<128,则由相应的字节值表示(与Unicode转ASCII字节一样)
  • 如果代码点数值>=128,则将其转换为一个2个字节,3个字节或4个字节的序列,该序列的每个字节都在128到255之间。


简单总结一下

编码(encode):将Unicode字符串(中的代码点)转换特定字符编码对应的字符串的过程和规则。

解码(decode):将特定字符编码的字节串转换为对应的Unicode字符串(中的代码点)的过程和规则。

3.Python源代码文件的执行过程

我们都知道,磁盘上的文件都是以二进制格式存放的,其中文本文件都是以某种特定编码的字节形式存放的。

对于程序源代码文件的字符编码是由编辑器指定的,比如我们使用pycharm来编写Python程序时会指定文件编码为UTF-8,那么Python代码被保存到磁盘时就会被转换为UTF-8编码对应的字节(encode过程)后写入磁盘。

当执行Python代码文件中的代码时,Python解释器在读取Python代码文件中的字节串之后,需要将其转换为UNICODE字符串(decode过程)之后才执行后续操作。

文件头部声明:顶部的:#--coding:utf-8—这个并不是告诉你,用utf-8编码的,而是说如果代码中有中文注释,就需要此声明,不然中文显示不了

4.总结

对于Python代码中避免遇到编码问题,一点小建议:

  • 字符编码声明:在代码开头声明编码格式
  • 使用codes的open函数处理文本文件
  • 尽可能使用Unicode而不是str
  • 尽可能使用Python3。

学习Python的小伙伴可以关注下我的公众号,一起学习,共同进步,我会第一时间在公众号推送干货,相信不会让你失望的!

发布于 2019-06-11

文章被以下专栏收录

    专注Python Web开发、机器学习、运维、测试、安全、IT等技术在实际工作中的应用,成为全栈工程师必修之路。如您在工作中有很深的体会,也欢迎您把知识分享到专栏,为技术传播,让更多程序员得到普惠做出一份贡献。

    Python programming