解析Python requests响应内容编码规则

1.发现问题

我们在使用requests发送请求时,响应的内容有时候会出现乱码的情况,下面我举一个例子:

import requests

r = requests.get('http://www.baidu.com')
print(r.text)   # 打印发现内容为乱码

在这里插入图片描述

我们可以使用r.encoding来查看编码解析text时我们的字符集编码是什么:

print(r.encoding)

打印结果:
在这里插入图片描述
然后我们在通过r.text查看到HTML本身的字符集编码是utf-8,所以这里才会出现乱码的情况
在这里插入图片描述

2.解决问题

2.1方式一

我们通过r.text查看到HTML或XML文本自身的字符集编码,然后通过r.encoding设置就行了

import requests

r = requests.get('http://www.baidu.com')
r.encoding = 'utf-8'
print(r.text)   # 此时发现打印的内容就不是乱码了

在这里插入图片描述

2.2方式二

我们可以通过r.apparent_encoding去拿到字符集编码(由 charset_normalizer 或 chardet 库提供的表观编码)。

import requests

r = requests.get('http://www.baidu.com')
r.encoding = r.apparent_encoding
print(r.text)   # 此时发现打印的内容就不是乱码了

在这里插入图片描述

3.分析问题

3.1 requests响应内容编码规则

我这里用的Pycharm,所以大家看步骤就好,有些快捷键可能不同的IDE不同。

  • 按两下Shift键,打开搜索
  • 搜索框输入utils,点击Classes
  • 选择requests下的utils,并点击
    在这里插入图片描述
  • Ctrl+F,搜索输入:get_encoding_from_headers
    在这里插入图片描述

这个函数就是requests的字符集编码规则获取方法,接下来我们分析一下里面的步骤(我用注释的方式来写):

def get_encoding_from_headers(headers):
    """Returns encodings from given HTTP Header Dict.

    :param headers: dictionary to extract encoding from.
    :rtype: str
    """

	# 我们通过传入的请求头来拿到content-type信息,一般响应如果有指定字符集都会跟在content-type,例如:content-type: application/x-www-form-urlencoded; charset=UTF-8
    content_type = headers.get('content-type')

	# 这一步主要是判断是否有返content_type,如果没有就直接返回None
    if not content_type:
        return None

	#这里主要是使用内置方法_parse_content_type_header将content_type和里面的charset分割开
    content_type, params = _parse_content_type_header(content_type)

	# 判断params里面有没有charset,也就是有的网站做的好的会跟上charset,跟上的话会直接返回跟上的字符集编码,我们上面请求的例子中你可以打印headers中的content-type,会发现没有charset
    if 'charset' in params:
        return params['charset'].strip("'\"")

	# 这里就是requests针对没有写charset的情况,给对应文本类型返回默认字符集,这就是为什么我们上面的例子使用r.encoding打印出的结果是ISO-8859-1
    if 'text' in content_type:
        return 'ISO-8859-1'

	# 这里就是针对没写charset,返回内容是json格式的时候,requests默认使用utf-8,所以一般使用json格式返回的内容用requests请求不容易乱码
    if 'application/json' in content_type:
        # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset
        return 'utf-8'

小结:
这里我们把为什么上面会出现乱码的问题重新分析了一下,因为访问后,它返回的content-type里面是没有写charset的,所以requests默认给了‘ISO-8859-1’编码,但它本身又是‘utf-8’所以会导致乱码。但是json格式的返回缺不容易出现乱码,因为json格式默认的就是‘utf-8’

3.2 r.apparent_encoding如何获取文本编码

  • 首先通过Ctrl+单击,点击r.apparent_encoding的apparent_encoding
  • 进入models.py模块
    在这里插入图片描述
  • 这里看不出什么,所以我们在深入一点,Ctrl+单击chardet.detect的detect
    在这里插入图片描述
    接下来我们分析一下里面的步骤(我用注释的方式来写):

def detect(byte_str: bytes) -> Dict[str, Optional[Union[str, float]]]:
    """
    chardet legacy method
    Detect the encoding of the given byte string. It should be mostly backward-compatible.
    Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it)
    This function is deprecated and should be used to migrate your project easily, consult the documentation for
    further information. Not planned for removal.

    :param byte_str:     The byte sequence to examine.
    """
    #这里是判断你传递来的字节字符串如果不是bytearray, bytes就抛出错误
    if not isinstance(byte_str, (bytearray, bytes)):
        raise TypeError(  # pragma: nocover
            "Expected object of type bytes or bytearray, got: "
            "{0}".format(type(byte_str))
        )

	#如果传递的是bytearray类型,就转换成bytes
    if isinstance(byte_str, bytearray):
        byte_str = bytes(byte_str)

	# 这里就是使用根据传入的字节字符串去匹配它最有可能的字符集
    r = from_bytes(byte_str).best()

	# 根据返回的内容回显到encoding
    encoding = r.encoding if r is not None else None
    language = r.language if r is not None and r.language != "Unknown" else ""
    confidence = 1.0 - r.chaos if r is not None else None

    # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process
    # but chardet does return 'utf-8-sig' and it is a valid codec name.
    if r is not None and encoding == "utf_8" and r.bom:
        encoding += "_sig"

    return {
    	# 这里就是用了一个三目,如果encoding不在CHARDET_CORRESPONDENCE这个字典里,就用原来的encoding,如果在字典里,就从字典里取出规范的名称(就是大写),你可以Ctrl+单击,点进去看有那些字符集编码
        "encoding": encoding
        if encoding not in CHARDET_CORRESPONDENCE
        else CHARDET_CORRESPONDENCE[encoding],
        "language": language,
        "confidence": confidence,
    }

4.总结

分析到这里差不多就结束了,如果上面文章有什么不对的,请评论区高手指出我改正。
文章仅个人学习笔记,欢迎大家一起交流沟通学习。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值