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

相关文章

慢sql提前分析预警和动态sql替换-Mybatis-SQL

《慢sql提前分析预警和动态sql替换-Mybatis-SQL》为防止慢SQL问题而开发的MyBatis组件,该组件能够在开发、测试阶段自动分析SQL语句,并在出现慢SQL问题时通过Ducc配置实现动... 目录背景解决思路开源方案调研设计方案详细设计使用方法1、引入依赖jar包2、配置组件XML3、核心配

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

Python中模块graphviz使用入门

《Python中模块graphviz使用入门》graphviz是一个用于创建和操作图形的Python库,本文主要介绍了Python中模块graphviz使用入门,具有一定的参考价值,感兴趣的可以了解一... 目录1.安装2. 基本用法2.1 输出图像格式2.2 图像style设置2.3 属性2.4 子图和聚

Python使用Matplotlib绘制3D曲面图详解

《Python使用Matplotlib绘制3D曲面图详解》:本文主要介绍Python使用Matplotlib绘制3D曲面图,在Python中,使用Matplotlib库绘制3D曲面图可以通过mpl... 目录准备工作绘制简单的 3D 曲面图绘制 3D 曲面图添加线框和透明度控制图形视角Matplotlib

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

基于Python打造一个智能单词管理神器

《基于Python打造一个智能单词管理神器》这篇文章主要为大家详细介绍了如何使用Python打造一个智能单词管理神器,从查询到导出的一站式解决,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 项目概述:为什么需要这个工具2. 环境搭建与快速入门2.1 环境要求2.2 首次运行配置3. 核心功能使用指

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获