python编码及requests乱码问题

摘要:
所以,也可以使用CP936表示GBK。MBCS是这些编码的统称。目前为止大家都是用了双字节,所以有时候也叫做DBCS。必须明确的是,MBCS并不是某一种特定的编码,Windows里根据你设定的区域不同,MBCS指代不同的编码,而Linux里无法使用MBCS作为编码。UCS还仅仅是字符对应码位的一张表而已,比如"汉"这个字的码位是6C49。字符具体如何传输和储存则是由UTF来负责。
1.字符编码简介

1.1. ASCII
ASCII(American Standard Code for Information Interchange),是一种单字节的编码。计算机世界里一开始只有英文,而单字节可以表示256个不同的字符,可以表示所有的英文字符和许多的控制符号。不过ASCII只用到了其中的一半(x80以下),这也是MBCS得以实现的基础。

1.2. MBCS
然而计算机世界里很快就有了其他语言,单字节的ASCII已无法满足需求。后来每个语言就制定了一套自己的编码,由于单字节能表示的字符太少,而且同时也需要与ASCII编码保持兼容,所以这些编码纷纷使用了多字节来表示字符,如GBxxx、BIGxxx等等,他们的规则是,如果第一个字节是x80以下,则仍然表示ASCII字符;而如果是x80以上,则跟下一个字节一起(共两个字节)表示一个字符,然后跳过下一个字节,继续往下判断。

这里,IBM发明了一个叫Code Page的概念,将这些编码都收入囊中并分配页码,GBK是第936页,也就是CP936。所以,也可以使用CP936表示GBK。

MBCS(Multi-Byte Character Set)是这些编码的统称。目前为止大家都是用了双字节,所以有时候也叫做DBCS(Double-Byte Character Set)。必须明确的是,MBCS并不是某一种特定的编码,Windows里根据你设定的区域不同,MBCS指代不同的编码,而Linux里无法使用MBCS作为编码。在Windows中你看不到MBCS这几个字符,因为微软为了更加洋气,使用了ANSI来吓唬人,记事本的另存为对话框里编码ANSI就是MBCS。同时,在简体中文Windows默认的区域设定里,指代GBK。

1.3. Unicode
后来,有人开始觉得太多编码导致世界变得过于复杂了,让人脑袋疼,于是大家坐在一起拍脑袋想出来一个方法:所有语言的字符都用同一种字符集来表示,这就是Unicode。

最初的Unicode标准UCS-2使用两个字节表示一个字符,所以你常常可以听到Unicode使用两个字节表示一个字符的说法。但过了不久有人觉得256*256太少了,还是不够用,于是出现了UCS-4标准,它使用4个字节表示一个字符,不过我们用的最多的仍然是UCS-2。

UCS(Unicode Character Set)还仅仅是字符对应码位的一张表而已,比如"汉"这个字的码位是6C49。字符具体如何传输和储存则是由UTF(UCS Transformation Format)来负责。

一开始这事很简单,直接使用UCS的码位来保存,这就是UTF-16,比如,"汉"直接使用x6Cx49保存(UTF-16-BE),或是倒过来使用x49x6C保存(UTF-16-LE)。但用着用着美国人觉得自己吃了大亏,以前英文字母只需要一个字节就能保存了,现在大锅饭一吃变成了两个字节,空间消耗大了一倍……于是UTF-8横空出世。

UTF-8是一种很别扭的编码,具体表现在他是变长的,并且兼容ASCII,ASCII字符使用1字节表示。然而这里省了的必定是从别的地方抠出来的,你肯定也听说过UTF-8里中文字符使用3个字节来保存吧?4个字节保存的字符更是在泪奔……(具体UCS-2是怎么变成UTF-8的请自行搜索)

2.python2.x 3.x编码问题
  1. 源码编码方式(.py文件的字符集),
  2. 执行编码方式(程序运行加载到内存的编码方式)
    python2.x默认执行编码方式为ascii 所以我们需要再py文件头加入一行代码来指定一个确定的执行编码方式(内存中加载的字符串使用的就是执行编码方式)
# -*- coding:utf-8 -*-

python3.x默认执行编码方式为utf-8所以无需增加此行代码

**无论是python2.7还是python3.6,执行编码方式和源码编码方式必须一致,虽然文件开头# --coding:xxx --用于指定执行编码方式,但是也会决定python解释器读取源文件时的编码方式,如果源文件是gbk格式,但是开头xxx为utf-8,那么python解释器将会以utf-8编码读取gbk格式的文件,严重时将会导致错误,所以,一定要保证xxx和源文件的编码方式一致,即执行编码方式和源码编码方式一致。

py2中python中任意两种不同字符编码的转换都是以unicode为基石
image.png

python2.x和python3.x中bytes、str和unicode
image.png

引用链接:https://blog.csdn.net/xiaoyink/java/article/details/80850448

3.requests获取网页乱码问题

运行环境python3.6
例如:

# -*- coding: utf-8 -*
import requests
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
            'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,ko;q=0.6'}

def main(url):
    res = requests.get(url,headers=headers)
    print(res.text)

if __name__ == '__main__':
    main('https://stock.hexun.com/2019-12-16/199691973.html?from=rss')

打印的结果html中文乱码:
image.png

原因:
requests会从服务器返回的响应头的 Content-Type 去获取字符集编码,如果content-type有charset字段那么requests才能正确识别编码,否则就使用默认的 ISO-8859-1. 一般那些不规范的页面往往有这样的问题.
res.encoding可查看requests识别的编码

res = requests.get(url,headers=headers)
print(res.encoding)
print(res.headers)
#ISO-8859-1
#{'Server': 'nginx', 'Date': 'Tue, 21 Apr 2020 02:14:57 GMT', 'Content-Type': 'text/html', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'Expires': 'Tue, 21 Apr 2020 02:15:57 GMT', 'Cache-Control': 'max-age=60', 'X-UA-Compatible': 'IE=EmulateIE7', 'Content-Encoding': 'gzip'}

如上打印的encoding和headers结果都验证了上面的说法。
修改该问题有2中方法:

  1. 强制修改res.encoding的编码类型
    requests的返回结果对象里有个apparent_encoding函数, apparent_encoding通过调用chardet.detect()来识别文本编码. 但是需要注意的是,这有些消耗计算资源.
    因为在源码中是用的content进行识别
@property
def apparent_encoding(self):
    """使用chardet来计算编码"""
    return chardet.detect(self.content)['encoding']

chardet.detect可用于识别文本编码

>>> data = '离离原上草,一岁一枯荣'.encode('gbk')
>>> chardet.detect(data)
{'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'}

>>> data = '离离原上草,一岁一枯荣'.encode('utf-8')
>>> chardet.detect(data)
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

confidence表示识别概率
  1. 采用res.content作为文本结果
    requests在获取网络资源后,我们可以通过两种模式查看内容。 一个是r.text,另一个是r.content,那他们之间有什么区别呢?
    分析requests的源代码发现,r.text返回的是处理过的Unicode型的数据,而使用r.content返回的是bytes型的原始数据。也就是说,r.content相对于r.text来说节省了计算资源,r.content是把内容bytes返回. 而r.text是decode成Unicode. 如果headers没有charset字符集的化,text()会调用chardet来计算字符集,这又是消耗cpu的事情.

所以直接将bytes的content传给BeautifulSoup进行解析便不用考虑编码问题,因为BeautifulSoup接收bytes或str的网页都会返回str的数据用于解析

soup = BeautifulSoup(text,'lxml')

引用文章:http://xiaorui.cc/archives/2786

免责声明:文章转载自《python编码及requests乱码问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇android 项目中设置背景图片【添加最新版本的mysql的jdbc连接jar包】java.math.BigInteger cannot be cast to java.lang.Long异常下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

import cx_Oracle ImportError: DLL load failed: 找不到指定的模块。

我们知道,Python使用cx_Oracle连接oracle的三个步骤是: 1、先安装cx-Oracle包,地址:https://pypi.org/project/cx-Oracle/5.3/#files 2、下载Oracle客户端instantclient-basic-win64-12.rar(我的电脑是64位,12是版本,还有11、10等),解压后将o...

python- 双层装饰器 字符串格式化 python模块 递归 生成器 迭代器 序列化

1.双层装饰器 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # author:zml LOGIN_INFO=False IS_ADMIN=False defcheck_log(func): definner(): res=func() ifLOGIN_INFO: print('验证成功!') return...

Linux 文件属性

1.在Linux中我们可以使用ll或者ls –l命令来显示一个文件的属性以及文件所属的用户和组 2."d"在Linux中代表该文件是一个目录文件,在Linux中第一个字符代表这个文件是目录、文件或链接文件等等。 当为[ d ]则是目录 当为[ - ]则是文件; 若是[ l ]则表示为链接文档(link file); 若是[ b ]则表示为装置文件里面的可...

ffmpeg转码时对编码率和固定码率的处理

http://www.rosoo.net/a/201107/14663.html 一般fps在代码里这样表示 Fps = den/num 如果den = 15,num=1,则fps = 15。 如果帧率固定,pts*fps 就表示当前是第几帧。 当输入视频流的帧率不固定,如rmvb ,而输出视频流的帧率固定,ffmpeg作如下处理(参考ffmpeg代码版本...

使用python操作zookeeper

kazoo 介绍 zookeeper的开发接口以前主要以java和c为主,随着python项目越来越多的使用zookeeper作为分布式集群实现,python的zookeeper接口也出现了很多,现在主流的纯python的zookeeper接口是kazoo。因此如何使用kazoo开发基于python的分布式程序是必须掌握的。 安装kazoo pip3 in...

ansible使用jinja2模板

jinja2基本语法 控制结构 {% %}             jinja2中的for循环用于迭代Python的数据类型,包括列表,元组和字典          2.变量取值 {{ }}             jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/...