Python接口自动化测试框架(扩展篇)-- requests源码分析:response类的text属性都干了啥,为啥中文乱码?

本文主要是介绍Python接口自动化测试框架(扩展篇)-- requests源码分析:response类的text属性都干了啥,为啥中文乱码?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景:前面有一篇关于requests请求响应中文乱码的解决办法,但是心中仍有些疑惑,还是想知道答案,不管是否发送请求定义了content-type:text/html;charset=utf-8请求头信息,还是响应的网页源码中有charset=utf-8字符集,经过试验:response类headers中根本就没有得到我们定义的字符集,还有response.encoding得到的也不是解析网页的charset设置的字符集,很是奇怪,下面来找源码分析一下:

首先我们来看requests的Response中的content源码:

@property
def content(self):"""Content of the response, in bytes."""if self._content is False:# Read the contents.if self._content_consumed:raise RuntimeError('The content for this response was already consumed')if self.status_code == 0 or self.raw is None:self._content = Noneelse:self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b''self._content_consumed = True# don't need to release the connection; that's been handled by urllib3# since we exhausted the data.return self._content

上面可以看出content属性始终没有关于encoding的输出,那么可以猜测requests是通过chardet去计算猜出编码,实际与预期不符!

而response的encoding是类属性,源码注释#:Encoding to decode with when accessing r.text.,是给text属性解码用的。所以更多情况使用content属性来接收网页响应源码,再解码一次即可得到正常的中文。

接下来再看text属性的源码:

    @propertydef text(self):"""Content of the response, in unicode.If Response.encoding is None, encoding will be guessed using``chardet``.The encoding of the response content is determined based solely on HTTPheaders, following RFC 2616 to the letter. If you can take advantage ofnon-HTTP knowledge to make a better guess at the encoding, you shouldset ``r.encoding`` appropriately before accessing this property."""# Try charset from content-typecontent = Noneencoding = self.encodingif not self.content:return str('')# Fallback to auto-detected encoding.if self.encoding is None:encoding = self.apparent_encoding# Decode unicode from given encoding.try:content = str(self.content, encoding, errors='replace')except (LookupError, TypeError):# A LookupError is raised if the encoding was not found which could# indicate a misspelling or similar mistake.## A TypeError can be raised if encoding is None## So we try blindly encoding.content = str(self.content, errors='replace')return content

中间有一个encoding=response的类属性self.encoding,再判断类属性的值是否为None,经调试:在if之前打印self.encoding类属性,对不起它是有值的:ISO-8859-1,所以就不会执行下面的代码计算encoding的值,这暂且不管,我们继续进入apparent_encoding它也是个属性,源码如下,并加入调试代码:调试return之前的东西:

    @propertydef apparent_encoding(self):"""The apparent encoding, provided by the chardet library."""print("这是个什么东西:{}".format(chardet.detect(self.content)))return chardet.detect(self.content)['encoding']

传入的是content属性的值(即接收的响应报文),输出的结果是:{'encoding': 'utf-8', 'language': '', 'confidence': 0.99},刚好返回的这个dict数据类型的encoding:utf-8,如果不出意外,self.encoding就该是utf-8,那text属性下面返回的content即是得到经过utf8解码的响应文本数据。

如果我在源码text属性中,直接将if条件设置为假,那么执行这个apparent_encoding属性,结果得到正常编码utf-8,不管你的网页响应是啥编码,基本都可以得到正确的中文输出!

所以此时我严重怀疑这是个bug,当然,requests大家还是用得好好的,怎么可能是个bug呢?继续深究。。。

那么就只剩下一个问题:在请求响应之后的encoding属性值是从哪里来的?为了一探究竟,再来看几处源码:

def get_encodings_from_content(content):"""Returns encodings from given content string.:param content: bytestring to extract encodings from."""warnings.warn(('In requests 3.0, get_encodings_from_content will be removed. For ''more information, please see the discussion on issue #2266. (This'' warning should only appear once.)'),DeprecationWarning)# print("content获取encoding:",content)charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I)pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I)xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]')return (charset_re.findall(content) +pragma_re.findall(content) +xml_re.findall(content))def _parse_content_type_header(header):"""Returns content type and parameters from given header:param header: string:return: tuple containing content type and dictionary ofparameters"""tokens = header.split(';')# print("拆分请求头:",tokens)content_type, params = tokens[0].strip(), tokens[1:]params_dict = {}items_to_strip = "\"' "for param in params:param = param.strip()if param:key, value = param, Trueindex_of_equals = param.find("=")if index_of_equals != -1:key = param[:index_of_equals].strip(items_to_strip)value = param[index_of_equals + 1:].strip(items_to_strip)params_dict[key.lower()] = valuereturn content_type, params_dictdef get_encoding_from_headers(headers):"""Returns encodings from given HTTP Header Dict.:param headers: dictionary to extract encoding from.:rtype: str"""# print("从请求头获取encoding:",headers)# headers={"content-type":"text/html;charset=utf-9"}content_type = headers.get('content-type')# print(content_type)if not content_type:return Nonecontent_type, params = _parse_content_type_header(content_type)if 'charset' in params:return params['charset'].strip("'\"")if 'text' in content_type:return 'ISO-8859-1'

不是bug,最终可以确定这个encoding属性是从util.py的get_encoding_from_headers方法中最后的if条件判断得到,至于为甚发送请求明明定义了content-type:text/html;charset=utf-8,为什么响应结果的headers却没有;charset=utf-8内容,还需要多多通晓源码,所以,最终我修改了源码:在text属性的if条件设置is not None不使用它的默认编码,text不要再像上篇文章使用编码再解码得到正确的中文输出。下面引入一个别人分析链接关于requests库中文编码问题 - 不止于python - 博客园,也是介绍requests请求响应中文乱码的问题。

这篇关于Python接口自动化测试框架(扩展篇)-- requests源码分析:response类的text属性都干了啥,为啥中文乱码?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1043814

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl