【接口自动化】-2- request模块及通过变量实现接口关联
一、发送请求的三种方式
第一种:直接调用简洁请求方法
requests.get ()、requests.post () 等是封装好的快捷方式,底层实际调用第二种 requests.request()
。
常用方法格式:
requests.get(url, params=None, **kwargs)
requests.post(url, data=None, json=None, **kwargs)
requests.put(url, data=None, **kwargs)
requests.delete(url, **kwargs)
第二种:底层通用方法
requests.request()
是更基础的实现,第一种的简洁方法底层调用它,需手动传 method
指定请求类型,格式:
requests.request(method, url, **kwargs)
第三种:带会话管理的请求
借助 requests.session()
维持会话,自动处理 cookie 关联,适合登录态延续等场景,格式:
requests.session().request( self, method, # 请求方式(如 'GET' 'POST' ) url, # 请求路径 params=None, # 查询参数 data=None, # 表单参数 json=None, # JSON 参数 files=None, # 文件参数 headers=None, # 请求头 cookies=None, # cookies 信息 # 还有 auth、timeout 等更多扩展参数
)
关系与区别:
调用关系:第一种调第二种,第二种调第三种 。
核心差异:第三种(session
方式)能自动维护 cookie ,前两种是独立请求,无会话关联能力 。
核心请求参数
method
:填 GET、POST 等 HTTP 请求方法,告诉服务器要执行啥操作(比如 GET 是获取资源,POST 是提交数据 )。
url
:接口的访问地址,服务器靠这个定位要请求的资源在哪 。
数据传递参数
params
:查询参数,会拼在 URL 后面(格式像 ?key1=value1&key2=value2
),常用于 GET 请求传数据,让服务器根据这些参数返回对应结果,比如按关键词搜索接口就可能用它传搜索词 。
data
:表单参数,Content-Type
一般是 application/x-www-form-urlencoded
,把数据以键值对形式(类似表单提交)发给服务器,POST 请求传简单键值数据常用它 。
json
:传 JSON 格式数据,Content-Type
是 application/json
,现在很多接口喜欢用这格式传复杂结构化数据(比如包含嵌套、数组的数据),后端好解析 。
files
:文件参数,Content-Type
为 multipart/form-data
,用来上传文件(像图片、Excel 等),比如接口需要上传用户头像时就用它 。
辅助配置参数
headers
:请求头,放一些元信息,比如告诉服务器客户端能接受啥数据格式(Accept
)、当前请求用的 token(Authorization
)、模拟浏览器请求的 User-Agent
等,让服务器更 “懂” 这个请求 。
cookies
:存 cookie 信息,有些接口靠 cookie 识别用户身份、维持会话,比如登录后拿 cookie 继续请求需要登录态的接口 。
其他功能参数
auth
:用来做鉴权,比如 Basic Auth 这种简单认证,填用户名、密码相关信息,让服务器验证请求是否有权限 。
timeout
:设置请求超时时间(单位一般是秒 ),超过这个时间没收到响应,就抛出超时异常,避免程序因接口卡着一直等 。
allow_redirects
:布尔值,控制是否自动处理重定向(比如 301、302 跳转 ),True
就自动跳,False
就停在当前响应 。
proxies
:配置代理,比如想通过代理服务器转发请求(隐藏真实 IP、走特定网络环境等 ),就填代理的地址(格式像 {"http": "http://代理IP:端口", "https": "https://代理IP:端口"}
)。
hooks
:钩子函数,在请求的不同阶段(比如发送前、收到响应后 )执行自定义逻辑,方便做一些额外操作(像记录请求耗时、修改响应数据 )。
stream
:布尔值,若为 True
,响应内容会以流的形式处理,适合下载大文件时逐步读取,不用一次性加载到内存,省内存 。
verify
:控制是否验证 SSL 证书,True
会严格验证(防止中间人攻击等 ),但遇到自签名证书(比如公司内部接口 )会报错,可设为 False
跳过验证(生产环境慎用,有安全风险 )。
cert
:指定客户端证书文件路径,有些安全要求高的接口,需要客户端带证书证明身份,就用它配置 。
关于 session
的特点
用 requests.session()
创建的会话,能自动处理 cookie 关联的接口!比如登录接口返回的 cookie ,后续同会话里的请求会自动带上,不用每次手动传,像浏览器保持登录态一样,比单独用 requests.get
/post
(这些是单次请求,cookie 不自动延续 )方便多了,做接口测试模拟用户连续操作(登录 → 访问个人中心 → 退出 )很实用 。
二、发送请求的参数细节(文字说明 + 代码块)
1. params
传参(查询字符串参数)
- 说明:参数拼在 URL 后(
?
连接,&
分隔多参数 ),会对内容做 urlencode 编码(转 ASCII 形式 )。 - 代码示例:
def test_login(self):params = {"name1": "baili","name2": "百里"}# 三种请求方式示例(按需选一种实际执行,这里仅演示传参写法 )requests.get(url="http://47.107.116.139/phpwind/", params=params )# requests.request("GET", "http://47.107.116.139/phpwind/", params=params)# requests.session().request("GET", "http://47.107.116.139/phpwind/", params=params)
2. data
传参(Form 表单参数)
- 说明:
- 自动加请求头
Content-Type: application/x-www-form-urlencoded
。 - 对参数做 urlencode 编码(转 ASCII 形式 )。
- 自动加请求头
- 代码示例:
def test_login(self):datas = {"name1": "baili","name2": "百里"}# 三种请求方式示例(按需选一种实际执行 )requests.get(url="http://47.107.116.139/phpwind/", data=datas )# requests.request("GET", "http://47.107.116.139/phpwind/", data=datas)# requests.session().request("GET", "http://47.107.116.139/phpwind/", data=datas)
3. json
传参(JSON 参数)
- 说明:
- 自动加请求头
Content-Type: application/json
。 - 对参数做 Unicode 编码 。
- 自动加请求头
- 代码示例:
def test_login(self):datas = {"name1": "baili","name2": "百里"}# 三种请求方式示例(按需选一种实际执行 )requests.get(url="http://47.107.116.139/phpwind/", json=datas )# requests.request("GET", "http://47.107.116.139/phpwind/", json=datas)# requests.session().request("GET", "http://47.107.116.139/phpwind/", json=datas)
4. files
传参(文件参数)
- 说明:
- 自动加请求头
Content-Type: multipart/form-data
。 - 以二进制形式把文件内容放到请求体,支持两种上传方式:
- 方式 1:直接打开本地文件 。
- 方式 2:构造虚拟文件元组(文件名、内容、文件类型 )。
- 自动加请求头
- 代码示例(单文件上传):
def test_login(self):# 方式 1:传本地实际文件datas = {"uploads": open("E:\\shu.png", "rb") }# 方式 2:构造虚拟文件datas = {"uploads": ("a.png", "阿萨德噶哈都干", "image/png") }# 三种请求方式示例(按需选一种实际执行 )requests.get(url="http://47.107.116.139/phpwind/", files=datas )# requests.request("GET", "http://47.107.116.139/phpwind/", files=datas)# requests.session().request("GET", "http://47.107.116.139/phpwind/", files=datas)
- 代码示例(多文件 + 混合传参):
def test_login(self):# 多文件上传datas = {"files": open("E:/shu.png", "rb"), "uploads": ("a.png", "阿萨德噶哈都干", "image/png") }# 同时传表单 + 文件参数 data1 = {"name1": "baili","name2": "百里"}data2 = {"files": open("E:/shu.png", "rb"),"uploads": ("a.png", "阿萨德噶哈都干", "image/png")}# 三种请求方式示例(按需选一种实际执行 )requests.get(url="http://47.107.116.139/phpwind/", data=data1, files=data2 )# requests.request("GET", "http://47.107.116.139/phpwind/", data=data1, files=data2)# requests.session().request("GET", "http://47.107.116.139/phpwind/", data=data1, files=data2)
三、接口关联
⭐ 接口关联基础逻辑
“接口关联” 就是 前一个接口的返回值,作为后一个接口的入参 ,让多个接口能按业务流程串联(比如登录接口返回 token,后续订单接口用该 token 鉴权)。要实现它,得先从第一个接口响应里精准提取需要的数据,这就用到正则、JsonPath 这类工具。
⭐ 正则表达式(Regex)—— 处理字符串响应
当接口返回是文本 / HTML / 普通字符串,用正则提取数据。Python 里靠 re
库实现,常用方法:
1. 核心方法
re.search(pattern, string)
:- 功能:在字符串里找第一个匹配正则的内容,返回 “匹配对象”(没匹配到返回
None
)。 - 取值:用
.group(1)
提取正则里括号捕获的内容(括号是 “捕获分组”,把想要的数据圈出来 )。
- 功能:在字符串里找第一个匹配正则的内容,返回 “匹配对象”(没匹配到返回
re.findall(pattern, string)
:- 功能:提取所有匹配正则的内容,返回 “列表”(没匹配到返回空列表 )。
- 取值:用下标
[0]
取第一个结果(若只有一个匹配,列表就一个元素 )。
2. 代码示例(结合图中案例)
假设 res.text
是接口返回的文本(比如含 csrf_token
的 HTML 或字符串):
import re# 场景:从响应里提取 csrf_token 值
res_text = '...<input name="csrf_token" value="abc123xyz" />...' # 模拟接口返回# 1. re.search 用法
search_result = re.search('name="csrf_token" value="(.*?)"', res_text)
if search_result:token = search_result.group(1) # 取括号里的内容 → "abc123xyz"# 2. re.findall 用法
findall_result = re.findall('name="csrf_token" value="(.*?)"', res_text)
if findall_result:token = findall_result[0] # 取列表第一个元素 → "abc123xyz"
- 正则语法里,
(.*?)
是 “非贪婪匹配”,尽可能短地匹配内容,避免把多余字符包含进来(比如防止截取到无关内容 )。 - 适用场景:接口返回是 HTML 页面、非 JSON 格式字符串,需要提取特定字段时用。
⭐ JsonPath —— 处理 JSON 响应
当接口返回是 JSON 格式(很常见,比如登录返回的 token、用户信息 ),用 JsonPath 提取更方便、直观。Python 里可通过 jsonpath
库(需提前 pip install jsonpath
安装 )实现。
1. 语法规则(核心)
$
:根节点(表示整个 JSON 数据,提取时从这开始定位 )。.
:取子节点(类似 “点语法”,逐层访问 JSON 的键 )。- 列表取值:用
[下标]
取列表里的元素(下标从 0 开始 )。 ..
:递归取值(深度遍历,找所有符合条件的节点 )。
2. 代码 + 案例
假设接口返回的 JSON 数据如下(先转成 Python 字典 / JSON 结构 ):
json_data = {"access_token": "72_Elind8uyQPa_bGMm3nNfEc_ebvrRhvR8thlO-OBA6DkeykyJNOlgXt4far1to9qIoUWzrvT8ws-EWItFNFinIN1XRFUF1jNW-k6169c-yfPlmBOqdmTFFJaV3DgANSeAEAVIN","expires_in": 7200,"mashang": [{"name": "百里"}, {"name": "北凡"}]
}
3. 常用提取场景
取单个字段:
from jsonpath import jsonpath# 取 access_token → 结果:列表形式,取 [0] 拿到值 access_token = jsonpath(json_data, '$.access_token')[0] # 取 expires_in → 结果:[7200] → 取 [0] 得 7200 expires_in = jsonpath(json_data, '$.expires_in')[0]
取列表里的元素:
# 取 mashang 列表第一个元素 → 结果:[{"name": "百里"}] → 取 [0] 得字典 first_mashang = jsonpath(json_data, '$.mashang[0]')[0] # 取第一个元素的 name → 结果:["百里"] → 取 [0] 得 "百里" first_name = jsonpath(json_data, '$.mashang[0].name')[0]
递归取值(
..
用法):# 找所有 name 字段 → 结果:["百里", "北凡"] → 取 [0] 得 "百里"(或遍历列表拿全部) all_names = jsonpath(json_data, '$..name')[0]
4. 方法特点
jsonpath.jsonpath()
提取结果是列表:
- 匹配到数据 → 列表存结果(一个结果就长度为 1,多个结果按顺序存 )。
- 没匹配到 → 返回
False
(注意不是None
,判断时要留意 )。
⭐ 实际接口自动化流程(串联思路)
- 发送第一个接口请求(比如登录),拿到响应(字符串 / JSON )。
- 用 正则 / JsonPath 提取需要的数据(如 token、用户 ID )。
- 把提取的数据,作为第二个接口的入参(放到
params
/data
/json
/headers
里 )。 - 发送第二个接口请求,实现 “接口关联”。
举个简化的登录 → 获取用户信息流程:
import requests
import re
from jsonpath import jsonpath# 1. 登录接口(假设返回 JSON,含 token)
login_url = "https://example.com/login"
login_data = {"username": "test", "password": "123"}
login_res = requests.post(login_url, data=login_data)
login_json = login_res.json() # 转 JSON 字典# 2. 用 JsonPath 提取 token
token = jsonpath(login_json, '$.access_token')[0]# 3. 第二个接口(用 token 访问用户信息)
info_url = "https://example.com/user/info"
headers = {"Authorization": f"Bearer {token}"} # 把 token 放请求头
info_res = requests.get(info_url, headers=headers)# 4. 从用户信息接口提取数据(假设返回 HTML,用正则)
user_name = re.search('<span class="username">(.*?)</span>', info_res.text).group(1)
print(f"提取的用户名:{user_name}")
四、上手练习!完整的实战演练
import re
import jsonpath
import requestsclass TestApi:# 通过类变量csrf_token = ""access_token = ""sess = requests.session() # 必须是同一个session回话,保持会话状态def test_phpwind(self):urls = "http://47.107.116.139/phpwind/"# 使用session发起get请求res = TestApi.sess.request(method="get", url=urls)# 正则提取csrf_tokensearch_value = re.search('name="csrf_token" value="(.*?)"', res.text)TestApi.csrf_token = search_value.group(1)print(TestApi.csrf_token)def test_login(self):urls = "http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"datas = {"username": "baili","password": "baili123","csrf_token": TestApi.csrf_token, # 从第1个接口的返回值中提取"backurl": "http://47.107.116.139/phpwind/","invite": ""}headerss = {"Accept": "application/json, text/javascript, /; q=0.01","X-Requested-with": "XMLHttpRequest"}# 使用session发起post请求res = TestApi.sess.request(method="post", url=urls, data=datas, headers=headerss)print(res.json())def test_get_token(self):url = "https://api.weixin.qq.com/cgi-bin/token"params = {"grant_type": "client_credential","appid": "wx74a8627810cfa308","secret": "e40a02f9d79a8097df497e6aaf93ab80"}# 使用session发起get请求res = TestApi.sess.request(method="get", url=url, params=params)# JsonPath提取access_tokenjsonpath_value = jsonpath.jsonpath(res.json(), '$.access_token')TestApi.access_token = jsonpath_value[0]print(jsonpath_value[0])def test_edit_flag(self):url = "https://api.weixin.qq.com/cgi-bin/tags/update"params = {"access_token": TestApi.access_token}data = {"tag": {"id": 134, "name": "广东人"}}# 使用session发起post请求res = TestApi.sess.request(method="post", url=url, params=params, json=data)print(res.json())
1. csrf_token和access_token为什么是类变量?
这两个token作为接口关联的参数,必须作为全局变量,才能让所有的函数都使用。因为在其中一个函数中创建的变量出了该函数的作用域就会失效,要是在别的函数里面用就是创建新的变量了,无法做到接口参数关联。
2. 为什么要sess = requests.session()来作为请求方式?
因为request.session.request()可以自动处理 cookie 关联,保持会话状态。
3. TestApi.csrf_token = search_value.group(1)
因为csrf_token是类变量,若使用必须经过类名.去调用。search_value是正则表达式提取出来的结果,.group(1)返回对象值。
4. res.json()
当接口返回的是 JSON 格式数据(响应头中通常有 Content-Type: application/json
)时,res.json()
会自动解析这个 JSON 字符串,返回对应的 Python 数据结构(字典 dict
或列表 list
),方便后续用 Python 语法(如 []
、.
)提取数据。
5. TestApi.access_token = jsonpath_value[0]
assess_token是类变量,jsonpath_value的返回值是一个列表,[0]是为了取列表中的值(去中括号)