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

相关文章

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

2024.6.24 IDEA中文乱码问题(服务器 控制台 TOMcat)实测已解决

1.问题产生原因: 1.文件编码不一致:如果文件的编码方式与IDEA设置的编码方式不一致,就会产生乱码。确保文件和IDEA使用相同的编码,通常是UTF-8。2.IDEA设置问题:检查IDEA的全局编码设置和项目编码设置是否正确。3.终端或控制台编码问题:如果你在终端或控制台看到乱码,可能是终端的编码设置问题。确保终端使用的是支持你的文件的编码方式。 2.解决方案: 1.File -> S

[职场] 公务员的利弊分析 #知识分享#经验分享#其他

公务员的利弊分析     公务员作为一种稳定的职业选择,一直备受人们的关注。然而,就像任何其他职业一样,公务员职位也有其利与弊。本文将对公务员的利弊进行分析,帮助读者更好地了解这一职业的特点。 利: 1. 稳定的职业:公务员职位通常具有较高的稳定性,一旦进入公务员队伍,往往可以享受到稳定的工作环境和薪资待遇。这对于那些追求稳定的人来说,是一个很大的优势。 2. 薪资福利优厚:公务员的薪资和

Python 字符串占位

在Python中,可以使用字符串的格式化方法来实现字符串的占位。常见的方法有百分号操作符 % 以及 str.format() 方法 百分号操作符 % name = "张三"age = 20message = "我叫%s,今年%d岁。" % (name, age)print(message) # 我叫张三,今年20岁。 str.format() 方法 name = "张三"age

springboot家政服务管理平台 LW +PPT+源码+讲解

3系统的可行性研究及需求分析 3.1可行性研究 3.1.1技术可行性分析 经过大学四年的学习,已经掌握了JAVA、Mysql数据库等方面的编程技巧和方法,对于这些技术该有的软硬件配置也是齐全的,能够满足开发的需要。 本家政服务管理平台采用的是Mysql作为数据库,可以绝对地保证用户数据的安全;可以与Mysql数据库进行无缝连接。 所以,家政服务管理平台在技术上是可以实施的。 3.1

iptables(7)扩展模块state

简介         前面文章我们已经介绍了一些扩展模块,如iprange、string、time、connlimit、limit,还有扩展匹配条件如--tcp-flags、icmp。这篇文章我们介绍state扩展模块  state          在 iptables 的上下文中,--state 选项并不是直接关联于一个扩展模块,而是与 iptables 的 state 匹配机制相关,特

一道经典Python程序样例带你飞速掌握Python的字典和列表

Python中的列表(list)和字典(dict)是两种常用的数据结构,它们在数据组织和存储方面有很大的不同。 列表(List) 列表是Python中的一种有序集合,可以随时添加和删除其中的元素。列表中的元素可以是任何数据类型,包括数字、字符串、其他列表等。列表使用方括号[]表示,元素之间用逗号,分隔。 定义和使用 # 定义一个列表 fruits = ['apple', 'banana

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

python实现最简单循环神经网络(RNNs)

Recurrent Neural Networks(RNNs) 的模型: 上图中红色部分是输入向量。文本、单词、数据都是输入,在网络里都以向量的形式进行表示。 绿色部分是隐藏向量。是加工处理过程。 蓝色部分是输出向量。 python代码表示如下: rnn = RNN()y = rnn.step(x) # x为输入向量,y为输出向量 RNNs神经网络由神经元组成, python

python 喷泉码

因为要完成毕业设计,毕业设计做的是数据分发与传输的东西。在网络中数据容易丢失,所以我用fountain code做所发送数据包的数据恢复。fountain code属于有限域编码的一部分,有很广泛的应用。 我们日常生活中使用的二维码,就用到foutain code做数据恢复。你遮住二维码的四分之一,用手机的相机也照样能识别。你遮住的四分之一就相当于丢失的数据包。 为了实现并理解foutain