python自动化测试:request模块

python的requests模块

requests是 Python 中最流行的 HTTP 客户端库,以简洁优雅的 API 著称,用于发送 HTTP/HTTPS 请求。以下是其核心使用场景和常用方法详解:

一、核心使用场景

  1. API 调用
    • 与 RESTful API 交互(如获取天气数据、股票行情、社交媒体接口)。
    • 示例:调用 GitHub API 获取用户信息。
  2. 网页内容抓取(Web Scraping)
    • 获取网页 HTML 源码,配合解析库(如 BeautifulSoup)提取数据。
  3. 文件上传/下载
    • 上传图片、文档到服务器;下载文件(如 PDF、图片)。
  4. 自动化测试
    • 模拟用户请求,测试 Web 服务的功能和性能。
  5. 会话管理
    • 维持登录状态(Cookies)、处理跨请求参数(如 CSRF Token)。
  6. 处理认证
    • 支持 Basic Auth、OAuth、JWT 等认证机制。

二、常用方法详解

1. 发送 GET 请求(获取数据)

import requests

# 基础用法
response = requests.get("https://api.example.com/data")

# 带查询参数 (自动编码为 ?key1=value1&key2=value2)
params = {"page": 2, "limit": 10}
response = requests.get("https://api.example.com/data", params=params)

# 自定义请求头 (如模拟浏览器)
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(url, headers=headers)
  • 关键属性
    • response.status_code:HTTP 状态码(200、404 等)
    • response.text:响应内容(字符串)
    • response.json():解析 JSON 响应(自动转换字典)
    • response.content:二进制响应内容(如下载图片)

2. 发送 POST 请求(提交数据)

# 表单数据 (application/x-www-form-urlencoded)
data = {"username": "admin", "password": "12345"}
response = requests.post("https://api.example.com/login", data=data)

# JSON 数据 (application/json)
json_data = {"name": "John", "age": 30}
response = requests.post("https://api.example.com/users", json=json_data)

3. 其他 HTTP 方法

requests.put(url, data={})     # 更新资源
requests.delete(url)            # 删除资源
requests.head(url)              # 获取响应头(无正文)
requests.patch(url, data={})    # 部分更新资源

4. 文件上传

files = {"file": open("photo.jpg", "rb")}
response = requests.post("https://api.example.com/upload", files=files)

5. 会话管理(Session)

with requests.Session() as s:
    # 登录一次,后续请求自动携带 Cookies
    s.post("https://example.com/login", data={"user": "a", "pass": "b"})
    # 访问需要登录的页面
    response = s.get("https://example.com/dashboard")

6. 超时与异常处理

try:
    # 设置超时(连接+读取时间)
    response = requests.get(url, timeout=(3.05, 27))
except requests.exceptions.Timeout:
    print("请求超时!")
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

三、高级功能

功能 代码示例
代理设置 proxies = {"http": "http://10.10.1.10:3128"}; requests.get(url, proxies=proxies)
SSL 证书验证 requests.get(url, verify=False)# 忽略证书错误(慎用!)
重定向控制 requests.get(url, allow_redirects=False)
流式下载大文件 response = requests.get(url, stream=True); for chunk in response.iter_content(chunk_size=8192): ...

四、最佳实践

  1. 总是检查状态码

    if response.status_code == 200:
        print("Success!")
    elif response.status_code == 404:
        print("Not Found!")
    
  2. 使用上下文管理器释放资源

    with requests.get(url, stream=True) as r:
        r.raise_for_status()  # 检查错误
    
  3. 避免阻塞主线程

    在异步框架(如 asyncio)中使用 aiohttp替代。


五、安装

pip install requests

注意:Requests 仅支持 Python 3.6+,对于异步需求可搭配 httpxaiohttp

通过灵活组合上述方法,requests能高效处理绝大多数 HTTP 交互场景,是 Python 网络编程的首选工具之一。

为什么测试场景优先使用requests.session

在测试场景(尤其是 Web 应用测试)中,优先使用 request.session(或 requests.Session())而非直接发送请求,主要基于以下核心原因:


一、核心原因:维持会话状态(Session State)

  1. Cookie 持久化
    • 用户登录后,服务器通过 Set-Cookie返回身份凭证(如 Session ID)
    • 后续请求需自动携带 Cookie 才能保持登录态
    • Session对象自动存储并发送 Cookie,无需手动处理
  2. 模拟真实用户行为
    • 真实用户操作是连续的会话过程(登录 → 浏览 → 下单)
    • Session确保所有请求在同一个会话上下文中执行

二、测试场景下的具体优势

1. 身份认证自动化

# 使用 Session 实现登录态保持
session = requests.Session()
login_data = {"user": "test", "pass": "123"}
session.post("https://example.com/login", data=login_data)  # 登录

# 后续请求自动携带 Cookie
profile = session.get("https://example.com/profile")  # 访问个人页
orders = session.get("https://example.com/orders")    # 查看订单

2. 跨请求数据共享

  • CSRF Token 处理

    某些网站在登录后返回动态 Token,需在后续请求的 Header 中携带:

    # 首次请求获取 Token
    resp = session.get("https://example.com/form")
    csrf_token = extract_token(resp.text)  # 从HTML中提取
    
    # 后续请求自动携带 Token
    session.headers.update({"X-CSRFToken": csrf_token})
    session.post("https://example.com/submit", data={...})
    

3. 连接复用提升性能

  • Session底层使用 HTTP 持久连接(Keep-Alive)
  • 减少 TCP 握手开销,比多次独立请求快 2-3 倍

4. 统一配置管理

# 全局设置 Headers/Proxies/Auth
session = requests.Session()
session.headers.update({
    "User-Agent": "MyTestAgent/1.0",
    "Accept-Language": "en-US"
})
session.proxies = {"https": "http://proxy.example.com:8080"}

# 所有请求自动继承配置
resp1 = session.get("/api/data")
resp2 = session.post("/api/update", json={...})

三、对比:直接使用 requests.get()的缺陷

# 错误示范:每次请求都是新会话
resp_login = requests.post(login_url, data=...)  # 获得 Cookie
resp_profile = requests.get(profile_url)        # ❌ 丢失登录 Cookie!
  • 每次请求独立:无法维持登录状态
  • 手动处理 Cookie:需从响应中提取并设置到后续请求
  • 重复配置:每个请求都要单独设置 Headers/Auth

四、典型测试场景案例

场景:测试电商下单流程

def test_checkout_flow():
    # 1. 创建会话
    s = requests.Session()
    
    # 2. 登录
    s.post("/login", data={"user": "buyer", "pwd": "secret"})
    
    # 3. 添加商品到购物车(自动携带登录态)
    s.post("/cart/add", json={"product_id": 1001})
    
    # 4. 获取购物车(验证商品已添加)
    cart = s.get("/cart").json()
    assert len(cart["items"]) > 0
    
    # 5. 提交订单(使用同一会话)
    order_resp = s.post("/order/submit", json={"address": "Test St"})
    assert order_resp.status_code == 200

五、技术实现原理

sequenceDiagram
    participant Test as 测试脚本
    participant Session as requests.Session
    participant Server as 被测服务器
    
    Test->>Session: 创建 Session 对象
    Session->>Server: 发送登录请求 (携带初始 Cookie)
    Server-->>Session: 返回 Set-Cookie (SessionID)
    Session->>Session: 存储 Cookie 到 CookieJar
    Test->>Session: 发送后续请求
    Session->>Server: 自动附加 Cookie
    Server-->>Session: 返回授权后的响应

六、最佳实践建议

  1. 始终优先使用 Session

    除非单次独立请求(如公开 API 调用)

  2. 结合上下文管理器

    with requests.Session() as s:
        s.post("/login", ...)
        s.get("/dashboard")
    # 退出时自动关闭连接
    
  3. 敏感操作隔离

    不同测试用例使用独立 Session,避免状态污染

  4. 清理会话数据

    测试结束后调用 session.close()释放资源

📌 关键结论:在测试场景中,request.session(或 requests.Session())本质是模拟真实用户的浏览器会话,通过维护 Cookie、连接和配置,确保测试流程符合实际业务逻辑。这是 Web 功能测试和爬虫开发的核心实践。

跨域

基本理解

好的,我们来详细介绍一下 跨域。这是一个在前端开发和后端开发中都非常核心的概念。

一、什么是跨域?

跨域 是指一个域下的文档或脚本试图去请求另一个域下的资源时,所发起的 HTTP 请求。

这里的“域”是由 协议、域名、端口 三者共同组成的。只要这三者中有任何一个不同,就构成了跨域。

举个例子:

假设你的前端项目运行在 http://localhost:3000,它要请求的后端 API 地址是 http://api.example.com:8080

我们来分析一下:

  • 协议: httpvs http-> 相同
  • 域名: localhostvs api.example.com-> 不同
  • 端口: 3000vs 8080-> 不同

由于域名端口都不同,所以当前端发起请求到后端时,就会产生跨域问题。

同源策略(Same-Origin Policy) 是浏览器的一个核心安全机制。它规定:浏览器默认只允许当前页面的脚本访问与其同源的资源。跨域请求正是被这个策略所限制的。


二、为什么会有跨域问题?(同源策略的目的)

同源策略主要是为了防止恶意网站通过 JavaScript 窃取其他网站的敏感数据(比如你的银行网站 Cookie)。

这里的同源就是上面说的相同的域

一个经典的例子:

  1. 你登录了 your-bank.com,浏览器保存了你的登录状态(Cookie)。
  2. 此时你又打开了一个恶意网站 evil-site.com
  3. 如果没有同源策略,evil-site.com上的恶意脚本就可以向 your-bank.com发起请求(比如转账),并且浏览器会自动带上你在银行的 Cookie,从而冒充你进行操作。
  4. 同源策略阻止了这种行为,因为 evil-site.comyour-bank.com不同源。

所以,同源策略是必要的,它保护了用户的安全。 但在现代 Web 开发中,我们常常需要安全地实现跨域通信,这就引出了解决方案。


三、常见的跨域解决方案

跨域的解决方案主要分为两类:JSONPCORS 以及服务器代理等。其中 CORS 是现代 Web 开发中最主流、最推荐的方案。

1. JSONP(JSON with Padding)

  • 原理: 利用 <script>标签没有跨域限制的特性。通过动态创建 <script>标签,其 src指向目标 URL,并传递一个回调函数的名称。服务器收到请求后,将数据包裹在这个回调函数中返回。浏览器拿到响应后,会立即执行这个回调函数,从而实现数据的获取。
  • 优点: 兼容性非常好,支持老式浏览器。
  • 缺点:
    • 只支持 GET 请求。
    • 存在安全风险(可能遭受 XSS 攻击)。
    • 需要前后端同时配合修改代码。
  • 现状: 随着 CORS 的普及,JSONP 已逐渐被淘汰,仅在一些遗留系统中使用。

2. CORS(Cross-Origin Resource Sharing,跨域资源共享)

  • 原理: 这是 W3C 标准,也是目前解决跨域问题的主流方案。它通过服务器在响应头中添加特定的字段,来告诉浏览器:“我允许来自某个源的请求访问我的资源”。浏览器会检查这些响应头,如果符合条件,则允许前端 JavaScript 访问响应数据。

  • 关键点: CORS 的实现主要在服务器端

  • 简单请求 vs 预检请求:

    • 简单请求: 满足特定条件(如 GET、HEAD、POST 方法,且 Content-Type 为 text/plain, multipart/form-data, application/x-www-form-urlencoded等)的请求,不会触发预检。
    • 预检请求(Preflight Request): 对于非简单请求(如使用 PUT、DELETE 方法,或 Content-Type: application/json),浏览器会先自动发送一个 OPTIONS方法的请求到服务器,询问是否允许实际请求。服务器必须响应允许后,浏览器才会发送真正的请求。
  • 服务器端设置示例(以 Node.js/Express 为例):

    const express = require('express');
    const app = express();
    
    // 方式一:允许所有来源的跨域请求(不推荐用于生产环境)
    app.use((req, res, next) => {
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
      res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      next();
    });
    
    // 方式二:允许特定来源的跨域请求(推荐)
    app.use((req, res, next) => {
      const allowedOrigins = ['http://localhost:3000', 'https://my-frontend-app.com'];
      const origin = req.headers.origin;
      if (allowedOrigins.includes(origin)) {
        res.header('Access-Control-Allow-Origin', origin);
      }
      res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
      res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      res.header('Access-Control-Allow-Credentials', 'true'); // 如果需要携带 Cookie
      next();
    });
    
    • Access-Control-Allow-Origin: 指定允许访问资源的源。*表示任意源,但为了安全,最好指定明确的源。
    • Access-Control-Allow-Credentials: 设置为 true时,表示允许前端请求携带 Cookie。此时 Access-Control-Allow-Origin不能为 *,必须指定明确的源。

3. 服务器代理(Proxy)

  • 原理: 在开发环境中,前端开发服务器(如 webpack-dev-server, Vite)可以配置一个代理。当前端请求一个同源的接口时,开发服务器会将这个请求转发到真正的后端服务器上。因为服务器之间的请求没有同源策略限制,所以可以成功获取数据,然后再返回给前端。

  • 示例(Vite 配置):

    // vite.config.js
    export default defineConfig({
      server: {
        proxy: {
          '/api': { // 将以 /api 开头的请求代理到 target
            target: 'http://api.example.com:8080',
            changeOrigin: true,
            rewrite: (path) => path.replace(/^\/api/, '') // 可选:重写路径
          }
        }
      }
    })
    

    前端代码只需请求 /api/user,Vite 开发服务器会将其代理到 http://api.example.com:8080/user

  • 优点: 对前端代码透明,无需修改后端代码。是解决开发环境跨域的利器。

  • 生产环境: 可以通过 Nginx 等反向代理服务器实现同样的效果。

4. 其他方案

  • WebSocket: WebSocket 协议本身不受同源策略限制。
  • postMessage: 主要用于窗口间(iframe、弹出窗口)的跨域通信。

四、总结

方案 原理 优点 缺点 适用场景
JSONP 利用 <script>标签无跨域限制 兼容性好 只支持 GET,不安全 老旧系统,兼容 IE9 以下
CORS 服务器设置响应头授权 标准、安全、功能强大 需后端配合,有复杂请求预检 现代 Web 开发首选
代理 开发服务器转发请求 前端零修改,配置简单 仅限开发环境或需 Nginx 配置 开发环境必备,生产环境可用 Nginx

核心要点:

  1. 跨域是浏览器行为,服务器之间不存在跨域问题。
  2. 根本解决方案是 CORS,需要后端开发者正确设置响应头。
  3. 开发阶段使用代理是解决跨域最方便的方法。
  4. 理解同源策略是为了保护用户安全,而 CORS 是在此基础上提供的一种受控的例外通道。

同源策略的具体执行方式

您问到了一个非常关键的点!您的直觉很接近,但实际情况比“发现就阻止”要稍微复杂一些。

简单回答:不是的。浏览器并不是简单地“发现就阻止”,而是通过一个两阶段的检查流程来实施同源策略,尤其是在处理像 XMLHttpRequest/fetch 这样的“复杂请求”时。

下面我们来详细拆解这个过程,您就会明白为什么需要 CORS 以及它是如何工作的。

同源策略的两种处理方式

浏览器对同源策略的执行,根据请求的类型和危险程度,分为两种情况:

1. 简单请求:直接发出请求,但在接收响应时进行拦截

如果一个请求满足了所有“简单请求”的条件(例如,方法是 GET/HEAD/POST,且 Content-Type 为 text/plain, application/x-www-form-urlencoded, multipart/form-data等),浏览器会:

  1. 正常发出请求:浏览器不会阻止请求的发出,它会像发送同源请求一样,将请求发送到服务器。
  2. 检查响应头:当服务器的响应返回给浏览器时,浏览器会审查响应头
  3. 决定是否放行数据
    • 如果响应头中包含 Access-Control-Allow-Origin并且该值匹配当前页面的源(或为通配符 *),浏览器就认为服务器“同意”这次跨域访问,于是将响应数据交给前端 JavaScript。
    • 如果响应头中没有 Access-Control-Allow-Origin,或者该值与当前源不匹配,浏览器就会拦截响应,并在控制台报错。此时,前端 JavaScript 拿不到任何响应数据,但开发者工具的网络面板里能看到服务器其实已经返回了 200 OK 的数据。

2. 非简单请求(预检请求):先“请示”,再“行动”

对于“非简单请求”(例如,使用了 PUT/DELETE 方法,或者 Content-Type: application/json),浏览器认为这可能是一个高风险操作,所以它引入了一个叫做 “预检请求(Preflight Request)” 的安全机制。这个过程清晰地展示了同源策略并非“一刀切”地阻止,而是一个协商过程。

其完整流程如下:

第①步:浏览器自动发起 OPTIONS 预检请求

在发送真正的业务请求之前,浏览器会自动地、悄悄地先向服务器发送一个 OPTIONS方法的请求。

这个 OPTIONS请求就像是去服务器那里“敲门请示”,它里面包含了几个关键的头部信息,用来询问服务器:

  • Origin: 表明我(浏览器)这次请求的真正来源是哪个域。
  • Access-Control-Request-Method: 告诉服务器,我接下来的真实请求打算用什么 HTTP 方法(比如 PUT)。
  • Access-Control-Request-Headers: 告诉服务器,我接下来的真实请求打算携带哪些自定义的头部(比如 X-Token)。

第②步:服务器响应预检请求

服务器收到这个 OPTIONS请求后,并不会处理业务逻辑,而是专门根据请求头里的信息来判断是否允许此次跨域。

服务器需要回应一个特殊的响应,包含以下关键头部:

  • Access-Control-Allow-Origin: 明确告知浏览器,我允许哪个源进行跨域访问。
  • Access-Control-Allow-Methods: 明确告知浏览器,我允许哪些 HTTP 方法(如 GET, POST, PUT)。
  • Access-Control-Allow-Headers: 明确告知浏览器,我允许哪些自定义头部。
  • Access-Control-Max-Age: (可选)告知浏览器,这个预检结果可以被缓存多久(比如 86400 秒),在此期间内,对于同样的跨域请求,不需要再次发送预检。

第③步:浏览器判断是否继续发送真实请求

浏览器拿到服务器对 OPTIONS请求的响应后,会检查上面的几个头部字段。

  • 如果服务器允许(例如,Access-Control-Allow-Origin包含了我的源),那么浏览器就会进入下一步。
  • 如果服务器拒绝或不回应,浏览器会直接拦截并报错,真实的业务请求永远不会被发出

第④步:浏览器发送真实的业务请求

预检通过后,浏览器才会按照原计划,发送那个真正的 PUT/DELETE或带有 application/jsonPOST请求。

第⑤步:浏览器检查真实请求的响应

最后,浏览器会用和“简单请求”相同的逻辑来处理服务器的响应:检查响应头中的 Access-Control-Allow-Origin等字段,以决定是否将数据交给前端 JavaScript。

总结与类比

为了帮助您更好地理解,我们可以做一个简单的类比:

  • 同源策略:就像一个国家的海关政策,原则上不允许外国人随便进出。
  • 简单请求:像一个普通游客想入境旅游。他可以直接买机票飞过去(发出请求),但入境时边检官员(浏览器)会检查他的签证(响应头中的 CORS 信息)。没签证就不让进(拦截数据)。
  • 非简单请求(预检):像一个商人想带特殊设备入境谈生意。他不能直飞,必须先发电报给外交部(发送 OPTIONS 预检请求),询问“我带A设备、用B方式谈判行不行?”。外交部批准了(服务器响应允许),他才能买机票带着设备过来(发送真实请求)。最后入境时还要再出示一次批文(检查最终响应的 CORS 信息)。

所以,同源策略的核心作用是“控制”,而不是单纯的“阻止”。它通过这种精细化的流程,既保障了安全性,又为合法的跨域通信提供了标准化的途径,而这个途径就是我们所说的 CORS

posted @ 2025-12-02 18:48  GDms  阅读(8)  评论(0)    收藏  举报