zoukankan      html  css  js  c++  java
  • python编码及requests乱码问题

    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

  • 相关阅读:
    HashMap与Hashtable的区别
    List集合、泛型、装箱拆箱
    关于集合
    统一建模语言
    自定义栈
    学习笔记
    如何优化limit
    mysql五大存储引擎
    [离散数学]2016.12.15周四作业
    [离散数学]2016.12.9周四作业
  • 原文地址:https://www.cnblogs.com/i-love-python/p/12747235.html
Copyright © 2011-2022 走看看