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测试之道》——杨燕林、朱对洲、石赟 编著,若有侵权请联系删除。

posted @ 2018-09-13 11:33  交流(Communication)  阅读(336)  评论(0编辑  收藏  举报