概念
文本,字符串:显示在屏幕,控制台,网页等等的Unicode字符.
字符集:为显示的字符集合分配一个独一无二的位置
字节流:存放在硬盘,磁盘,传输流中的字节数据形式
编码:encode,是将文本、字符串转换成字节流的过程
解码:decode,是将字节流转换成文本,字符串的过程
编码与解码是一个互逆过程
像字符’A’,显示在屏幕中就是A本身,但是由于计算机是二进制的,只能存取0和1(但是读写按照字节读取),所以要将’A’保存到硬盘,要按照给定的字符集(默认Unicode)的位置’\x41’,使用"UTF-8"编码为0100 0001(2)存入硬盘,也就是字节流b’A’,
>>> '\x41' #Unicode字符集的位置
'A'
>>> bin(ord('\x41'))
'0b1000001' #二进制位置
>>> 'A'.encode("utf-8")
b'A' #字节流
>>> char='我'
>>> char.encode('utf-8')
>>> char.encode('gbk')
b'\xe6\x88\x91'
b'\xce\xd2'
在内存中暂存的所有的字符,都是Unicode编码,但只有往硬盘保存或者基于网络传输时,根据不同的编码规则,才能确定你输入的字符是英文还好汉文,这就是Unicode转换成其他编码格式的过程。
“好”.encode(‘ASCII’)
UnicodeEncodeError: ‘ascii’ codec can’t encode character ‘\u597d’ in position 0: ordinal not in range(128)
》》错误 .write方法默认写入字符串,不是byte数组
f.write(res.text.encode(‘utf-8’))
TypeError: write() argument must be str, not bytes
文件写入时,文件自动将unicode转为字节流存入文件中
encode(utf-8)将unicode转为字节流,所以不能将字节流直接存入 要将文件打开方式设置为wb+ 二进制模式才能写入
TypeError: a bytes-like object is required, not ‘str’。(倒过来 如果打开文件时是字节流 那么就不能写入str 而应该写入bytes 数组)
我们知道,GBK编码是一种单双字节变长编码,英文使用单字节编码,中文部分采用双字节编码
而UTF-8编码也是一种可变长编码,英文使用单字节编码,中文部分采用三字节编码.
例如:
char = '好'
print(char.encode('utf-8'))
print(char.encode('gbk'))
#输出
b'\xe5\xa5\xbd' #得到字节流
b'\xba\xc3'
注意:使用str.encode
函数作用是将Unicode字符编码为字节流
因此,gbk编码器在编码 \xe5\xa5\xbd\ 中,只编码单字节 \xe5,或者 \xe5\xa5,留下的字节 \xa5\xbd或者 \xbd 不能编码为gbk字符集中的字符,所以报错.
char = b'\xba\xc3'.decode('gbk') #将字节流编码为gbk字符
print(char)
char = b'\xe5\xa5\xbd'.decode('utf-8') #将字节流编码为utf-8字符
print(char)
#输出
好
好
#
```encode是一个逆向过程
"是".encode("gb2312")
b'\xca\xc7' 原本是字节流 将字节流当作字符来encode
b'\xca\xc7'.decode("gb2312")
'是'
### 思考与解决方案
所以为什么会出现这样的错误?
思考:**gbk编码器无法对字符'\xca'进行编码**,所以将页面的源代码```res.text```保存到新文件```get.html```中,推测源代码```res.text```使用UTF-8编码,而文件```get.html```使用GBK编码打开了
解决:将源代码和保存的文件都保持一致的编码
```python
#解决方案1,源代码保持utf-8,文件以utf-8编码打开
with open(r'd:\get.html', 'w', encoding='utf-8') as f:
f.write(res.text)
#解决方案2,文件保持gbk编码打开,源代码以gbk重新编码
with open(r'd:\get.html', 'wb') as f:
f.write(res.text.encode('gbk')) #显然人类在重复着相同的错误
源代码res.text
已经是utf-8形式了,所以gbk编码器依旧无法编码,
于是我们注意到,res.encoding
可以改变编码res.text
的输出方式
print(res.encoding)
#输出
ISO-8859-1 #一个被utf-8兼容的编码
#解决方案
res = requests.get('http://weibo.com')
res.encoding='gbk'
with open(r'd:\get.html', 'w') as f:
f.write(res.text)
关于网页的编码问题
在上述的解决方案1中,虽然解决了编码错误,但是保存的文件get.html
中,出现了乱码的问题:
因为源网页根本不是以utf-8编码,所以抓取得到的res.text
如果默认用utf-8编码自然会乱码.
所以抓取网页时能确保不会出现乱码的核心是: 保持编码一致
编码为X的源网页res=request.get
->编码为X的源代码res.text
->设置输出的编码为Xre.encoding
->保存到编码为X的文件get.html
在网页的头部<head>
中包含的元信息的标签<meta>
记录了该网页的字符集charset
,只要根据charset
设置res.encoding
以及保存文件的encoding=''
就能保证编码一致
<meta http-equiv="Content-type" content="text/html; charset=gb2312"/>
import re
re.search('charset=(.*?)[;"]',res.content).group(1) #使用正则表达式提取字符集