说到用python进行接口测试的话,可能第一个首选的就是requests库。这个大名鼎鼎的requests库,以其优雅简洁的实现方式彻彻底底征服了我,而且requests库的源码细细阅读起来,对于python的进阶之路都有莫大的帮助。那我今天就在这篇文章里面一起来由浅入深的简单入门一下requests库。
前几天呢我用django编写了一个实现用户管理的简单API,详细的可以看看使用django开发restful接口这篇博文。我们对于requests库的一个使用介绍呢,就从这个用户管理接口来开始。当然后续我会继续把locust参数化的文章也通过这篇django的用户管理接口来编写。
首先requests库是一个开源的库,我们可以在GitHub中随时获取他的源代码。https://github.com/psf/requests
通过官方的仓库我们可以了解到。requests最新版本目前支持的是python版本是2.7 3.5 3.6 3.7 3.8
。所以在安装之前一定要确认一下自己的python版本是否能够支持。
python安装库也非常的简单,我们通过pip来安装requests库。
pip install requests
安装好之后我们先来测试一下。
首先我们进入ipython的交互环境。
In [1]: import requests
In [2]: r = requests.get(‘https://www.baidu.com‘)
In [3]: r.text
Out[3]: ‘<!DOCTYPE html>\r\n<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>?\x99??o|??\x80??\x8b??\x8c??\xa0?°±?\x9f¥é\x81\x93</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div cl
ass=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=?\x99??o|??\x80??\x8b class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>?\x96°é\x97?</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao1 23</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>?\x9c°?\x9b?</a> <a href=http://v.baidu.com name=tj_trvide o class=mnav>è§\x86é¢\x91</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>è′′?\x90§</a> <noscript> <a hr
ef=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>?\x99???\x95</a> </noscript> <script>document.write(\‘<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=\‘+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ \‘" name="tj_login" class="lb">?\x99???\x95</a>\‘);\r\n </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">?\x9b′?¤\x9a?o§?\x93\x81</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <
p id=lh> <a href=http://home.baidu.com>?\x853?o\x8e?\x99??o|</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>????\x94¨?\x99??o|?\x89\x8d??\x85èˉ?</a> <a href =http://jianyi.baidu.com/ class=cp-feedback>?\x84\x8fè§\x81?\x8f\x8dé|\x88</a> ?o?ICPèˉ\x81030173?\x8f· <im
g src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>\r\n‘
可以看到我们的通过导入的requests库,仅仅三行代码,就通过get方法成功的返回了百度首页的数据。
在开始之前我们先把我们之前编写的用户管理API启动一下。
好了成功启动了我们开始把。
用户管理接口一共有三种表现形式。我们分别使用requests来请求试试。还是进入ipython的交互环境。
/user/
In [85]: r = requests.get(‘http://127.0.0.1:8000/user/‘)
In [86]: r.text
Out[86]: ‘{"code": 200, "message": "success", "data": [{"id": 3, "name": "\\u968f\\u98ce\\u6325\\u624b", "sex": 1, "idcard": "123", "email": "1084502012@qq.com", "address": "123", "company": "123", "created_time": "2020-11-11T14:33:12.869Z"}]}‘
可以看到我们的这个是什么成功返回了,但是格式好像不太正确,被单引号圈起来了,因为text返回的是一个文本字符串的格式。
但是我们的接口返回的是一个json格式的。
所以我们需要获取json数据。
In [87]: r.json()
Out[87]:
{‘code‘: 200,
‘message‘: ‘success‘,
‘data‘: [{‘id‘: 3,
‘name‘: ‘随风挥手‘,
‘sex‘: 1,
‘idcard‘: ‘123‘,
‘email‘: ‘1084502012@qq.com‘,
‘address‘: ‘123‘,
‘company‘: ‘123‘,
‘created_time‘: ‘2020-11-11T14:33:12.869Z‘}]}
可以看到这样就成功的返回了json格式的数据。
/user/1/
In [10]: r = requests.get(‘http://127.0.0.1:8000/user/3/‘)
In [11]: r.json()
Out[11]:
{‘code‘: 200,
‘message‘: ‘success‘,
‘data‘: {‘id‘: 3,
‘name‘: ‘随风挥手‘,
‘sex‘: 1,
‘idcard‘: ‘123‘,
‘email‘: ‘1084502012@qq.com‘,
‘address‘: ‘123‘,
‘company‘: ‘123‘}}
/user/?name=随风——params参数
那么这个参数是怎么去访问呢。get方法是可以在后面通过?key=value&key2=value2
的一个方式来进行请求的,那么我们先来看看这样的一个方式怎么样来进行的呢。
In [12]: r = requests.get(‘http://127.0.0.1:8000/user/?name=随风‘)
In [13]: r.json()
Out[13]:
{‘code‘: 200,
‘message‘: ‘success‘,
‘data‘: [{‘id‘: 3,
‘name‘: ‘随风挥手‘,
‘sex‘: 1,
‘idcard‘: ‘123‘,
‘email‘: ‘1084502012@qq.com‘,
‘address‘: ‘123‘,
‘company‘: ‘123‘,
‘created_time‘: ‘2020-11-11T14:33:12.869Z‘}]}
可以看到直接把参数附加在请求地址的后面也可以进行访问。
那有没有更加简便的方法呢。在requests库中提供了一个更加简单的方法。
首先我们阅读一下requests库的源码。下图。可以看到有一个params
参数,通过这个参数呢。
def get(url, params=None, **kwargs):
r"""Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary, list of tuples or bytes to send
in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
kwargs.setdefault(‘allow_redirects‘, True)
return request(‘get‘, url, params=params, **kwargs)
那我们来试试这种方式把。
In [17]: payload = {‘name‘:‘随风‘}
In [18]: r = requests.get(‘http://127.0.0.1:8000/user/‘,params=payload)
In [19]: r.json()
Out[19]:
{‘code‘: 200,
‘message‘: ‘success‘,
‘data‘: [{‘id‘: 3,
‘name‘: ‘随风挥手‘,
‘sex‘: 1,
‘idcard‘: ‘123‘,
‘email‘: ‘1084502012@qq.com‘,
‘address‘: ‘123‘,
‘company‘: ‘123‘,
‘created_time‘: ‘2020-11-11T14:33:12.869Z‘}]}
可以看到这种方式更简洁清晰明了。推荐使用这种方式。
当然同时也支持多个值的同时查找,在键值对中指定值为列表的方式。
In [20]: payload = {‘name‘:[‘随‘,‘风‘]}
In [21]: r = requests.get(‘http://127.0.0.1:8000/user/‘,params=payload)
In [22]: r.json()
Out[22]:
{‘code‘: 200,
‘message‘: ‘success‘,
‘data‘: [{‘id‘: 3,
‘name‘: ‘随风挥手‘,
‘sex‘: 1,
‘idcard‘: ‘123‘,
‘email‘: ‘1084502012@qq.com‘,
‘address‘: ‘123‘,
‘company‘: ‘123‘,
‘created_time‘: ‘2020-11-11T14:33:12.869Z‘}]}
post请求一般都是提交一个表单,在requests的post请求中提供了两个参数。看下源码吧。
def post(url, data=None, json=None, **kwargs):
r"""Sends a POST request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request(‘post‘, url, data=data, json=json, **kwargs)
分别是data
和json
,这两个都是传递参数的。其实data传的是表单参数,json传递的是content_type=application/json的数据类型的。所以我们先来试试吧。先试一下json参数的post请求。
In [32]: data = {‘name‘:‘随风挥手11‘,‘sex‘:1,‘idcard‘:‘1234567‘,‘email‘:‘1084502012@qq.com‘,‘address‘:‘123‘,‘company‘
...: :‘123‘}
In [33]: r = requests.post(‘http://127.0.0.1:8000/user/‘,json=data)
In [34]: r.json()
Out[34]:
{‘code‘: 201,
‘message‘: ‘created‘,
‘data‘: {‘id‘: 4,
‘name‘: ‘随风挥手11‘,
‘sex‘: 1,
‘idcard‘: ‘1234567‘,
‘email‘: ‘1084502012@qq.com‘,
‘address‘: ‘123‘,
‘company‘: ‘123‘}}
可以看到使用json参数成功的创建了一个新的用户。然后我们使用data参数来试试。
可以看到报错了,因为太长了,所以我这里直接截图了。
那么我们怎么使用这个data参数呢。我手里面也没有合适的示例,直接用官方的演示吧。
In [40]: payload = {‘key1‘: ‘value1‘, ‘key2‘: ‘value2‘}
In [41]: r = requests.post("http://httpbin.org/post", data=payload)
In [42]: r.text
Out[42]: ‘{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "key1": "value1", \n "key2": "value2"\n }, \n "headers": {\n "Accept": "*/*", \n "Accept-Encoding": "gzip, deflate", \n "Content-Length": "23", \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "httpbin.org", \n "User-Agent": "python-requests/2.24.0", \n "X-Amzn-Trace-Id": "Root=1-5fabff48-70ea1f742434542615593b4b"\n }, \n "json": null, \n "origin": "39.144.1.194", \n "url": "http://httpbin.org/post"\n}\n‘
这样通过data参数就成功提交了一个表单参数。
def put(url, data=None, **kwargs):
r"""Sends a PUT request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request(‘put‘, url, data=data, **kwargs)
然后我们试一下put请求得一个方式。
In [51]: r = requests.put(‘http://127.0.0.1:8000/user/4/‘,json=data)
In [52]: r.json()
Out[52]:
{‘code‘: 200,
‘message‘: ‘updated‘,
‘data‘: {‘id‘: 4,
‘name‘: ‘随风挥手11111‘,
‘sex‘: 1,
‘idcard‘: ‘1234567‘,
‘email‘: ‘1084502012@qq.com‘,
‘address‘: ‘123‘,
‘company‘: ‘123‘}}
可以看到数据成功更新了。
delete方法源码
def delete(url, **kwargs):
r"""Sends a DELETE request.
:param url: URL for the new :class:`Request` object.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request(‘delete‘, url, **kwargs)
删除刚才创建的数据试试。
In [63]: r = requests.delete(‘http://127.0.0.1:8000/user/5/‘)
In [64]: r.status_code
Out[64]: 204
因为delete是删除方法,所以我们只需要查看一下它的状态码是204即可。至此我们的四种请求方式就说完了。
在代码里面我们使用的r.text
和r.json()
都是requests提供的响应方法。
我们结合官方文档在这里总结一下常用的响应结果吧。
方法 | 描述 |
---|---|
r.text | Requests 会自动解码来自服务器的内容。大多数 unicode 字符集都能被无缝地解码。 |
r.json() | 返回json数据,如果数据不符合json格式规范,则会抛出异常 |
r.encoding | 查看返回的数据使用了什么编码 |
r.content | 使用字节的方式返回数据,通常用于图片等 |
r.status_code | 返回服务器响应的状态码,如200,400,500 |
r.raw | 返回响应的原始内容 |
r.headers | 返回服务器的响应头 |
r.url | 返回请求的地址数据 |
r.history | 返回请求的历史记录 |
这些都是requests库响应内容提供的一些方法。
session意思就是会话,在很多的场景中都是存在依赖关系的,比如我之前些的接口测试框架中智学网的接口就是我需要先登录才可以访问其他的接口,那么我必须保持这登录结果到下一个请求中。我们先来看一个示例。
import requests
s = requests.Session()
r1 = s.get(‘http://httpbin.org/cookies/set/sessioncookie/123456789‘)
print(r1.text)
#out: ‘{"cookies": {"sessioncookie": "123456789"}}‘
r2 = s.get("http://httpbin.org/cookies")
print(r2.text)
#out ‘{"cookies": {"sessioncookie": "123456789"}}‘
没有使用session会话保持的方式:
import requests
r1 = requests.get(‘http://httpbin.org/cookies/set/sessioncookie/123456789‘)
print(r1.text)
#out: ‘{"cookies": {"sessioncookie": "123456789"}}‘
r2 = requests.get("http://httpbin.org/cookies")
print(r2.text)
#out ‘{"cookies": {}}‘
由于手头没有示例,所以只能先拿官方的示例来敷衍一下了。。。
requests库已经很好用了,但是我们在这个基础上还是要进行一层封装,为什么要封装呢。
首先,封装可以更好的去集成到测试框架中,减少我们的代码数量,并且我们只要要通过封装的类实例化一次session会话,不用担心会话重复的问题。在者我们可以和测试框架的其他模块进行耦合,所以在这种前提下,封装requests必不可少,即使他已经足够好用了。
以下是我的一个封装示例。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import json
import urllib3
import requests
from requests.exceptions import RequestException
urllib3.disable_warnings()
class HttpRequest:
"""requests方法二次封装"""
http_method_names = [‘get‘, ‘post‘, ‘put‘,
‘patch‘, ‘delete‘, ‘head‘, ‘options‘, ‘trace‘]
def __init__(self):
self.timer = None
self.timeout = 30.0
self.session = requests.session()
def __call__(self, *args, **kwargs):
"""发送request请求"""
return self.request(*args, **kwargs)
def request(self, method: str, url: str, **kwargs):
"""发送请求"""
method = method.lower()
try:
response = self.dispatch(method, url, **kwargs)
self.timer = response.elapsed.total_seconds()
return response
except Exception as e:
raise e
def dispatch(self, method, *args, **kwargs):
if method in self.http_method_names:
handler = getattr(self.session, method.lower())
return handler(*args, **kwargs)
else:
raise AttributeError("请求方法不正确!")
req = HttpRequest()
if __name__ == ‘__main__‘:
r = req("get", url="https://www.baidu.com")
print(r.text)
运行一下:
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>???o|???e=????? ?°±?é</title></head>
成功返回了请求的内容。
本篇就先到这里吧,后面我估计会改造这篇文章的,里面有一些内容说的不是很详细,比如session怎么实现会话保持的,本来我想带你们看看源码的但是时间有点晚了。累了,以后在弄吧,我先休息了额。
原文:https://www.cnblogs.com/wxhou/p/13961825.html