python测试之道>第5章>模拟网络请求
python发送网络请求是通过模块中的方法来实现的,模块定义好了请求的语法格式,只需要按照相应的语法格式对号入座写代码即可。
一般来说我们测试的都是基于HTTP和HTTPS的网络请求,python有很多自动带的原生模块和扩展模块均可以模拟网络请求。当然我们在实际工作中会遇到很多其他协议请求,如WebSocket等。
本章以HTTP协议为例,采用原生模块和扩展模块对比的方式,给大家剖析一下原理。原理性的东西虽然广泛不被重视,但它对大家之后的学习是十分有好处的。
5.1 发送HTTP请求
5.1.1 requests模块
python有很多模块都可以发送HTTP请求,包括原生的模块http.client,urlib2等,第三方模块requests等,都封装了发送HTTP请求的方法。由于原生的模块过于复杂,不推荐使用,之后所有的请求都是基于第三方模块requests进行的,该模块的好处在于简单,把请求的框架都搭建好了,只需要填入相应的参数数据,就能发送网络请求了。
requests模块非原生模块,所有要使用还需要先进行安装,按照之前安装模块的方法,打开命令行(在运行中输入cmd),然后在命令行输入:
pip install requests
接前就会自动安装requests模块及其相关模块,然后就可以引入该模块使用其提供的方法了。
import requests
后面会通过一些实例来介绍requests的具体使用方法。
5.1.2 请求与响应
HTTP就是发送请求和获取响应的一个过程,而requests模块只需要一步就能完成这样的一个过程,并且request支持所有的HTTP请求的方法和响应数据,先进行语法介绍:
r = requests.方法(url, headers, data, ...)
其中url参数为必填的,毕竟HTTP请求就是对指定的URL进行发送,其他各种参数可根据实际请求的需要选择性使用。
发送请求后会获取响应结果,然后把结果赋值给变量,最后通过变量的属性值取出需要的结果,下面就是常用返回结果:
r.headers 获取返回的头信息
r.text 获取返回的主体(其实就是网页html)
r.cookies 获取返回的cookies
r.status_code 获取返回的状态码(通常会通过状态码判断请求是否成功)
学会requests的请求方法和获取响应的结果,就可以开始网络请求的测试了,下面就通过代码实例来熟悉requests的模块吧。
5.1.3 请求参数
1.URL参数
URL是唯一的必填参数,既然是网络请求,必须要有URL地址才能发送,就像快递一定要写目的地才能发件一样。先从最简单的HTTP请求讲起,请求一个静态页面,比如访问网易首页,通过抓包可以看到该请求是用的get方式发送的,所有要调用requests的get()方法来发送请求。
以网易首页作为请求例子,实例代码:
import requests # 导入requests模块;
test_url = "http://www.163.com" # 定义url
response = requests.get(test_rul) # 实例化requests.get()方法
print(response.status_code) # 输出返回的状态码
print(response.headers) # 输出返回的头信息
print(response.text) # 输出返回的主体
代码说明:
1 导入requests模块;
2 将网易首页的URL赋值到变量test_url中(这样的好处在于看起来清晰,也方便代码今后的维护);
3 在get()方法中将变量test_url直接传入,即完成了带URL的get请求(结果会赋值到变量reponse中,reponse中包含了返回结果的所有数据,可以根据需要获取想要的数据);
4、5、6 获取并打印返回的结果(状态码、头信息、主体)
运行结果如下图:
可以看出通过requests发送的HTTP请求只有3行代码,就那么简单的一行代码完成整个请求。
整个请求过程全被封装在方法之中,对于没有编码基础的测试人员来说再适合不过了。
2、headers 参数
headers是最常用的参数之一,前面那个例子只是最简单的请求,没有带其他参数信息,而很多时候需要带入headers发送请求,才可以获取相应的请求结果。
还是以网易首页作为例子,如果是pc端的请求,会返回pc端的页,如果是手机端的请求,则返回手机端的页面,这时候就要带上headers参数的请求,通过浏览器的抓包工具可以看到headers有个字段“User-Agent",而服务器就是根据这个字段来判断访问的来源,如果需要模拟手机端请求,需要将“User-Agent"改为请求的手机型号。
实例代码:
import requests
test_url = "http://www.163.com"
h = {"User-Agent":"Android/H60-L01/4.4.2"} # 将headers赋值到变量h中;
reponse = requests.get(test_url, headers=h) # 在get()方法中加一个headers参数,然后将变量赋值给headers参数。
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
运行结果如下:
3、cookies 参数
cookies也是最常用的参数之一,因为只要涉及登录后数据获取,都需要用到cookies参数,那么如何获取到cookies呢,一种是通过post发送登录请求,获取返回值的cookies属性(这个会在后面的实例中讲解),还有一种就是通过浏览器的网络抓包方式获取,在还不会熟练使用request模块的前提下,就先介绍如何通过抓包获取cookies。
首先打开chrome浏览器,然后登录网易邮箱(或其它网站),接着按F12(或在网页右键,选择检查)打开开发者工具,抓取cookies的界面如下图:
选择application中的cookies,点击对应的测试URL域名,再找到session属性并且domain是对应测试请求的域名,然后这条信息name和value对应的就是cookies了。
cookies参数以字典的形式发送,只需要对应的将name和value传入即可。
实例代码:
import requests
c = {"JSESSIONID":"abcRPAFmBY_sqf2qRMmxw"}
test_url = "https://www.163.com/"
reponse = requests.get(test_url, cookies=c)
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代码说明:
2 将浏览器抓取的cookies信息以字典的形式赋值到变量c之中;
4 在get方法中加一个cookies参数,然后将变量c赋值给cookies参数,即完成了带着cookies信息的get请求。
其实cookies也是可以通过headers参数传递的,只是不同之处在于cookies是以字典的形式发送的,而在headers之中cookies只是其中一个键,所以需要把cookies放到该键对应的值里面,而对应的值是以key=value的形式传入的,改一下代码。
import requests
c = {"JSESSIONID":"abcRPAFmBY_sqf2qRMmxw"}
test_url = "https://www.163.com/"
reponse = requests.get(test_url, headers={"cookies":c}) # 此处cookies是作为headers的键值传入;
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代码说明:
4 在get方法中将变量c赋值给headers参数中的cookies字段,即完成了带着cookies信息的get请求。
以上的两种方法,推荐使用cookies参数,一方面把cookies单独分离出来,不用与其他headers的字段放在一起,让代码更清晰,另一方面通过post请求返回的cookies是可以直接赋值到cookies参数之中,不需要再做转换。
4、params 参数
对于params参数可以存放请求的表单,并会以key1=value1&key2=value2&key3=value3的形式跟进URL之后发送,为了区分URL和参数,最好不要把表单放在URL之中,可以通过params参数进行发送, 上面网易邮箱的URL也是带着参数的,直接放在URL之中,如果使用params参数就可以把后面的参数和URL分离。
还是以博客园登录界面为例,只是URL部分把参数进行分离,代码如下:
import requests
# test_url = "https://passport.cnblogs.com/user/signin?ReturnUrl=https%3A%2F%2Fwww.cnblogs.com%2F"
test_url = "https://passport.cnblogs.com/user/signin"
p = {"ReturnUrl":"https%3A%2F%2Fwww.cnblogs.com%2F"}
reponse = requests.get(test_url, params=p)
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代码说明:
4 将需要发送表单以字典的形式赋值到变量p之中。
5 在get方法中加一个params的参数,然后将变量p赋值给params参数,即完成了带着params信息的get请求。
如果不使用params参数,也可以直接把表单加在URL中,只是代码不清晰,所以不推荐直接把表单加在URL中。
5、data 参数
data参数也是用于存放请求表单,是request模块中最重要的参数之一。
在使用data之前,先来了解一下post提交数据类型,区别与params,params只有一种类型(字符串类型),而post可以提交4种类型的数据,至于需要提交什么类型的数据,取决于服务器接收的数据类型。post的数据类型需要和服务器接收的一致,不然服务器就无法正常识别post的数据,导致测试结果报错。就像协议一样,接收方预定接收哪种类型的表单,然后发头方按照接收方指定的协议发送表单,这样就完成了一个表单的提交。
如何识别服务器接收的数据类型?最简单的方式还是抓包。抓取数据类型的界面如下:
通过抓包可以看到,在request headers 中有一个content-type的字段,这个字段表示了post发送数据的类型,一般分为以下4种类型:
第1种:content-type:application/json
实际上,现在越来越多的人反application/json作为请求头,用来告诉服务器消息主体是序列化后的json字符串。由于json规范流行,除了低版本IE之外的各大浏览器都原生支持json.stringify,服务器端语言也都有处理json的函数,并且json格式支持比键值对更加复杂的结果化数据。
第2种:content-type:application/x-www-form-urlencoded
这是最常见的post提交数据的方式,浏览器的原生form表单,如果不设置enctype属性,那么最终就会以application/x-www-form-urlencoded方式提交数据。提交的数据按照key1=value1&key2=value2的方式进行编码,key和value都进行了URL转码,然后打包发送到服务器。
第3种:content-type:multipart/form-data
content-type为multipart/form-data方式,主要用于上传文件。需要注意的是同时form的enctype属性也要设置为multipart/form-data,才能正确提交并解析所传输的数据。
第4种:content-type:text/xml
它是一种使用http作为传输协议,XML作为编码方式的远程调用规范。考虑到XML结构还是过于臃肿,一般场景用json会更灵活方便,所以这种提交我们的工作中实际使用的不多,仅了解一下就可以了。
最常见的是 第2种 类型,直接将数据以{key:value}的字典形式赋值给表单,然后通过request.post()中的data参数传递就可以了。
以网易音乐为例,搜索一首歌曲,通过抓包可以看到content-type是application/x-www-form-urlencoded,网易音乐的数据类型抓取图如下:
搜索一首”一次就好“,通过抓包会发现有进行加密,那就直接把加密后的内容放到form之中发送出去,网易音乐的表单抓取如下图:
实例代码:
import requests
test_url = "https://music.163.com/weapi/search/suggest/web?csrf_token="
f = {"params":"ShQVLipkE8Y/p89iMoKOX5whEin1ZyoKeXZJJe+rBg+8mrEQT3RaAR5UP5B+ayoKrPIVzxsVuuFA1askCPcBzRJ6qOoPsPz06xStotZZGTmHrzaz1RcbiRpYOOrZl8NC",\
"encSecKey":"9ac26cbd1a87fd9790fb1cc2f0a475a5cd24be6b73e1b516f86b00ab58b568c0d4303eb6314bbedf5adedb968f047f44bb6ebe1fd9cfa09339a781d762e8aba0184c8a57dc53d1717f89d5c85d02e635cec9e7610fac4faac33838a3f299a1aa672390e4430b21ee2ea03e37aee53d17d70973933ffdf3fc7872101a9ff973da"\
}
reponse = requests.post(test_url, data=f)
print(reponse.status_code)
print(reponse.headers)
print(reponse.text)
代码说明:
3 将需要发送的表单以字典形式赋值到变量form之中。
4 在post方法中加一个data参数,然后将变量赋值给data参数,即完成了带着表单信息的post请求。
运行结果如下图:
以上内容来自《python测试之道》——杨燕林、朱对洲、石赟 编著,若有侵权请联系删除。