发布时间:2026/6/14 10:57:47
用Playwright拦截和修改网络请求:不只是抓包那么简单
在 Web 开发和测试领域抓包工具如 Fiddler、Charles 和 Chrome DevTools 早已成为开发者的标配。它们能让我们查看网络请求和响应内容帮助定位问题。但当我们需要更深度的网络控制 —— 比如自动化修改请求、模拟各种异常场景、与 UI 操作无缝集成时传统抓包工具就显得力不从心了。Playwright 作为现代浏览器自动化框架提供了一套强大而灵活的网络拦截 API。它不仅仅是一个 抓包工具更是一个能让你完全掌控浏览器网络层的 网络编程接口。通过 Playwright你可以在请求发出前修改它在响应到达应用前篡改它甚至完全替换整个网络通信流程。一、为什么 Playwright 的网络拦截与众不同传统抓包工具工作在系统代理或浏览器扩展层面而 Playwright 直接与浏览器的 DevTools 协议深度集成。这种架构带来了几个关键优势无侵入性不需要修改系统代理或安装浏览器扩展启动浏览器时自动配置时序精确拦截发生在浏览器内部比外部代理更早捕获请求自动化友好所有操作都可以通过代码控制完美融入自动化测试和爬虫流程上下文隔离每个浏览器上下文可以有独立的网络拦截规则互不干扰协议全面支持 HTTP/HTTPS、WebSocket、Fetch 和 XHR 等所有现代 Web 协议二、核心原理page.route () 是如何工作的Playwright 网络拦截的核心是page.route()方法。它允许你注册一个路由处理函数当浏览器发出匹配特定 URL 模式的请求时这个函数会被调用。typescript运行// 基本语法 await page.route(urlPattern, async (route) { // 在这里处理拦截到的请求 // route对象包含了请求的所有信息和操作方法 });URL 模式支持三种匹配方式精确匹配https://api.example.com/users通配符匹配**/api/users/*最常用正则表达式new RegExp(.*\\.json$)当一个请求被拦截时你有四种选择route.continue()允许请求继续发送可选修改请求头、URL 或请求体route.fulfill()直接返回一个自定义响应不发送真实请求route.abort()完全中止请求route.fetch()先获取真实响应修改后再返回给应用三、基础操作从拦截到修改3.1 拦截并记录所有请求最简单的用法是记录所有网络请求这比 Chrome DevTools 更适合自动化分析typescript运行import { chromium } from playwright; async function logAllRequests() { const browser await chromium.launch(); const page await browser.newPage(); // 拦截所有请求 await page.route(**/*, (route) { const request route.request(); console.log([${request.method()}] ${request.url()}); console.log( 资源类型: ${request.resourceType()}); console.log( 请求头: ${JSON.stringify(request.headers())}); // 继续请求 route.continue(); }); await page.goto(https://example.com); await browser.close(); } logAllRequests();3.2 修改请求在发送前篡改数据你可以在请求发送到服务器之前修改它的任何部分typescript运行// 修改请求头 await page.route(**/api/**, async (route) { const headers { ...route.request().headers() }; // 添加认证令牌 headers[Authorization] Bearer your-token-here; // 移除追踪头 delete headers[X-Tracking-Id]; // 修改User-Agent headers[User-Agent] Custom-Browser/1.0; await route.continue({ headers }); }); // 修改POST请求体 await page.route(**/api/login, async (route) { if (route.request().method() POST) { const postData JSON.parse(route.request().postData() || {}); // 修改用户名 postData.username modified-user; // 添加额外参数 postData.extraField injected-by-playwright; await route.continue({ postData: JSON.stringify(postData) }); } else { await route.continue(); } });3.3 修改响应在应用收到前篡改数据这是 Playwright 最强大的功能之一。你可以先获取真实的 API 响应修改其中的部分内容然后再返回给应用typescript运行// 修改API响应数据 await page.route(**/api/products, async (route) { // 先获取真实响应 const response await route.fetch(); // 解析JSON数据 const data await response.json(); // 修改数据将所有商品价格打五折 data.products data.products.map((product: any) ({ ...product, price: product.price * 0.5, discounted: true })); // 添加一个新商品 data.products.push({ id: 999, name: Playwright专属商品, price: 0.99, discounted: true }); // 返回修改后的响应 await route.fulfill({ response, // 保留原始响应的状态码和头信息 json: data // 替换响应体 }); }); // 修改HTML页面内容 await page.route(**/index.html, async (route) { const response await route.fetch(); let body await response.text(); // 注入自定义JavaScript body body.replace( /body, scriptalert(页面已被Playwright修改);/script/body ); await route.fulfill({ response, body }); });3.4 模拟响应完全替代真实 API当后端 API 尚未开发完成或不稳定时你可以直接返回模拟数据typescript运行// 模拟成功响应 await page.route(**/api/users, async (route) { await route.fulfill({ status: 200, contentType: application/json, body: JSON.stringify([ { id: 1, name: Alice, email: aliceexample.com }, { id: 2, name: Bob, email: bobexample.com } ]) }); }); // 模拟错误响应 await page.route(**/api/payment, async (route) { await route.fulfill({ status: 500, contentType: application/json, body: JSON.stringify({ error: 服务器内部错误, code: INTERNAL_SERVER_ERROR }) }); });3.5 中止请求阻止不必要的资源加载这对于加速测试和爬虫非常有用可以阻止图片、广告和统计代码的加载typescript运行// 阻止所有图片加载 await page.route(**/*.{png,jpg,jpeg,gif,svg}, (route) { route.abort(); }); // 阻止特定域名的请求 await page.route(**/*.google-analytics.com/**, (route) { route.abort(); }); // 根据资源类型阻止 await page.route(**/*, (route) { const resourceType route.request().resourceType(); if ([image, stylesheet, font].includes(resourceType)) { route.abort(); } else { route.continue(); } });四、高级应用场景超越基础抓包4.1 GraphQL 请求的精准拦截与修改GraphQL 请求通常都发送到同一个端点传统的 URL 匹配方式无法区分不同的操作。Playwright 可以根据请求体中的operationName进行精准拦截typescript运行// 拦截特定的GraphQL操作 await page.route(**/graphql, async (route) { const request route.request(); const postData JSON.parse(request.postData() || {}); // 根据操作名称区分处理 switch (postData.operationName) { case GetUserProfile: // 修改用户查询响应 const response await route.fetch(); const data await response.json(); data.data.user.name Modified Name; data.data.user.email modifiedexample.com; await route.fulfill({ response, json: data }); break; case CreateOrder: // 模拟订单创建失败 await route.fulfill({ status: 400, contentType: application/json, body: JSON.stringify({ errors: [{ message: 库存不足 }] }) }); break; default: // 其他请求正常发送 await route.continue(); } });4.2 WebSocket 通信的完全控制Playwright 不仅支持 HTTP 请求还能拦截和修改 WebSocket 通信typescript运行// 监听WebSocket连接 page.on(websocket, (ws) { console.log(WebSocket已连接: ${ws.url()}); // 监听发送的消息 ws.on(framesent, (event) { console.log(发送: ${event.payload}); }); // 监听接收的消息 ws.on(framereceived, (event) { console.log(接收: ${event.payload}); }); // 监听关闭事件 ws.on(close, () { console.log(WebSocket已关闭); }); }); // 你甚至可以发送自定义消息到WebSocket服务器 // 或者模拟服务器发送消息到客户端4.3 模拟网络条件测试弱网和离线场景Playwright 可以模拟各种网络条件包括 3G、4G、离线等typescript运行// 模拟3G网络 await page.context().setOffline(false); await page.context().setNetworkConditions({ offline: false, downloadThroughput: 750 * 1024 / 8, // 750 Kbps uploadThroughput: 250 * 1024 / 8, // 250 Kbps latency: 150 // 150ms延迟 }); // 模拟离线状态 await page.context().setOffline(true); // 恢复正常网络 await page.context().setNetworkConditions({ offline: false, downloadThroughput: -1, uploadThroughput: -1, latency: 0 });4.4 动态路由根据条件决定如何处理请求你可以在路由处理函数中实现复杂的条件逻辑typescript运行// 动态决定是否使用Mock数据 let useMockData true; await page.route(**/api/data, async (route) { if (useMockData) { // 返回Mock数据 await route.fulfill({ status: 200, contentType: application/json, body: JSON.stringify({ data: mock-data }) }); } else { // 使用真实API await route.continue(); } }); // 稍后可以切换模式 useMockData false;4.5 反爬虫绕过修改请求指纹许多网站使用请求指纹来检测爬虫。通过 Playwright 的网络拦截你可以修改这些指纹typescript运行// 模拟真实浏览器的请求头 await page.route(**/*, async (route) { const headers { ...route.request().headers() }; // 移除Playwright特有的头信息 delete headers[X-Playwright]; // 添加真实浏览器的头信息 headers[Accept-Language] zh-CN,zh;q0.9,en;q0.8; headers[Accept-Encoding] gzip, deflate, br; headers[Sec-Fetch-Dest] document; headers[Sec-Fetch-Mode] navigate; headers[Sec-Fetch-Site] none; headers[Sec-Fetch-User] ?1; headers[Upgrade-Insecure-Requests] 1; await route.continue({ headers }); });五、完整实战案例电商网站价格测试让我们通过一个完整的案例来展示 Playwright 网络拦截的强大功能。假设我们需要测试一个电商网站的价格显示和折扣计算功能typescript运行import { test, expect } from playwright/test; test.describe(电商价格测试, () { test.beforeEach(async ({ page }) { // 拦截商品列表API await page.route(**/api/products, async (route) { const response await route.fetch(); const data await response.json(); // 修改商品价格创建各种测试场景 data.products [ // 正常价格商品 { id: 1, name: 普通商品, price: 100, originalPrice: 100 }, // 打折商品 { id: 2, name: 打折商品, price: 80, originalPrice: 100 }, // 免费商品 { id: 3, name: 免费商品, price: 0, originalPrice: 50 }, // 高价商品 { id: 4, name: 高价商品, price: 99999, originalPrice: 99999 }, // 负数价格测试错误处理 { id: 5, name: 异常商品, price: -10, originalPrice: 100 } ]; await route.fulfill({ response, json: data }); }); // 拦截购物车API await page.route(**/api/cart, async (route) { if (route.request().method() POST) { const postData JSON.parse(route.request().postData() || {}); // 模拟添加商品到购物车成功 await route.fulfill({ status: 200, contentType: application/json, body: JSON.stringify({ success: true, cart: { items: [postData], total: postData.price, discount: 0 } }) }); } else { await route.continue(); } }); }); test(应该正确显示各种价格, async ({ page }) { await page.goto(/products); // 验证普通商品价格 await expect(page.locator([data-testidproduct-1] .price)).toHaveText(¥100.00); // 验证打折商品显示原价和现价 await expect(page.locator([data-testidproduct-2] .original-price)).toHaveText(¥100.00); await expect(page.locator([data-testidproduct-2] .current-price)).toHaveText(¥80.00); // 验证免费商品显示免费 await expect(page.locator([data-testidproduct-3] .price)).toHaveText(免费); // 验证高价商品正确格式化 await expect(page.locator([data-testidproduct-4] .price)).toHaveText(¥99,999.00); // 验证异常价格显示错误提示 await expect(page.locator([data-testidproduct-5] .price)).toHaveText(价格异常); }); test(应该正确计算购物车总价, async ({ page }) { await page.goto(/products); // 添加普通商品到购物车 await page.click([data-testidproduct-1] .add-to-cart); // 验证购物车总价 await expect(page.locator([data-testidcart-total])).toHaveText(¥100.00); // 添加打折商品到购物车 await page.click([data-testidproduct-2] .add-to-cart); // 验证购物车总价更新 await expect(page.locator([data-testidcart-total])).toHaveText(¥180.00); }); });六、最佳实践与常见陷阱6.1 最佳实践尽早注册路由在调用page.goto()或触发任何可能产生网络请求的操作之前注册路由使用精确的 URL 模式避免使用**/*拦截所有请求这会影响性能保持路由处理函数简洁复杂的逻辑应该提取到单独的函数中正确处理异步操作确保所有异步操作都使用await使用route.fetch()获取原始响应这样可以保留原始的状态码、头信息和 cookie在测试结束后清理路由使用page.unroute()移除不再需要的路由分层测试策略大多数测试使用 Mock 数据提高速度保留少量集成测试使用真实 API6.2 常见陷阱忘记调用route.continue()或route.fulfill()这会导致请求挂起页面永远加载不完修改响应时丢失 Content-Type 头确保在route.fulfill()中设置正确的contentType路由处理函数中的错误未被捕获使用 try-catch 块处理可能的异常多个路由匹配同一个请求Playwright 会按照注册顺序调用第一个匹配的路由在路由处理函数中进行耗时操作这会影响页面加载速度模拟响应时忽略 CORS 头如果跨域请求需要特定的 CORS 头确保在模拟响应中包含它们七、总结Playwright 的网络拦截功能远不止是一个简单的抓包工具。它提供了对浏览器网络层的完全控制让你能够在请求发出前修改任何部分在响应到达应用前篡改数据完全模拟 API 响应无需依赖后端模拟各种网络条件和异常场景拦截和修改 WebSocket 通信与 UI 自动化无缝集成这些能力使得 Playwright 成为现代 Web 应用开发和测试的必备工具。无论是前端开发、自动化测试还是网络爬虫Playwright 的网络拦截功能都能帮助你解决传统抓包工具无法解决的问题。随着 Web 应用变得越来越复杂对网络控制的需求也越来越高。掌握 Playwright 的网络拦截技术将让你在 Web 开发和测试中如虎添翼能够更高效地构建和维护高质量的 Web 应用。

相关新闻

LLM信息抽取实战:从认知重构到结构化输出的七道关卡
2026/6/14 9:57:47

LLM信息抽取实战:从认知重构到结构化输出的七道关卡

1. 这不是“调用API就完事”的信息抽取——它是一场对LLM底层认知能力的系统性拆解你有没有试过让大模型从一段会议纪要里抽取出“谁在什么时间、向谁、提出了哪项具体建议”?结果模型要么漏掉关键角色,要么把“建议”和“结论”混为一谈,甚至…

阅读更多
一文详解C++中运算符的使用
2026/6/14 9:57:47

一文详解C++中运算符的使用

一、算术运算符运算符描述把两个操作数相加-从第一个操作数中减去第二个操作数*把两个操作数相乘/分子除以分母%取模运算符,整除后的余数自增运算符,整数值增加 1–自减运算符,整数值减少 1通过下面的例子可以让我们更好的理解C中的运算符的意…

阅读更多
Mythos架构解析:大模型长链推理的动态能力释放机制
2026/6/14 9:57:47

Mythos架构解析:大模型长链推理的动态能力释放机制

1. 项目概述:一次被刻意“锁住”的能力跃迁如果你最近关注大模型前沿动态,大概率在技术社区、AI从业者群或邮件列表里见过“TAI #200”这个编号——它不是某篇论文的DOI,也不是某个开源项目的Release Tag,而是The AI Alignment Ne…

阅读更多
这款开源免费的B站下载神器,连4K弹幕都能一键搞定!
2026/6/14 11:57:48

这款开源免费的B站下载神器,连4K弹幕都能一键搞定!

软件获取 各大平台视频下载工具大全 Bili23-Downloader Win安装版根据提示安装,绿色版免安装解压即用 MacOS平台分为 M 芯片& intel(即仅带x64后缀)的版本,根据处理器选择拖入即装 Linux系统则根据命令形式打开安装 作者提…

阅读更多
从‘敏捷’到‘瀑布’,你的项目选对‘开发方法’了吗?一张图帮你搞定决策
2026/6/14 11:57:48

从‘敏捷’到‘瀑布’,你的项目选对‘开发方法’了吗?一张图帮你搞定决策

敏捷与瀑布之外:现代项目开发方法的战略选择框架在数字化转型浪潮中,技术负责人和产品经理们经常陷入开发方法选择的困境。会议室里,敏捷派高举"快速迭代"的大旗,传统派坚持"周密规划"的原则,而混…

阅读更多
别再纠结RAID 0/1/10/01了!一张图帮你搞定NAS、服务器磁盘阵列选型
2026/6/14 11:57:48

别再纠结RAID 0/1/10/01了!一张图帮你搞定NAS、服务器磁盘阵列选型

一图读懂RAID选型:从家庭NAS到企业级存储的实战指南每次打开购物网站准备买硬盘组建存储系统时,总会被各种RAID级别绕得头晕眼花?作为一位经历过无数次数据灾难恢复的存储工程师,我完全理解这种选择困难。本文将用最直观的方式&am…

阅读更多
这款开源PDF分割合并工具绿色版!太强了
2026/6/14 11:57:47

这款开源PDF分割合并工具绿色版!太强了

软件获取 PDF工具相关软件合集 图文办公党必备!这款免费神器,轻松拆分、合并PDF,工作效率翻倍! 这是一款非常强大的PDF处理工具---PDFsam Basic。 PDFsam Basic 是一款免费、开源的PDF处理工具,专注于PDF的拆分、合并…

阅读更多
MPC8260 ATM控制器ABR流控与OAM性能监控实现详解
2026/6/14 11:57:47

MPC8260 ATM控制器ABR流控与OAM性能监控实现详解

1. 项目概述与核心价值在ATM网络的世界里,有两个概念是工程师绕不开的:一个是确保网络不堵车的“交通警察”ABR流控,另一个是时刻监控网络健康状况的“体检医生”OAM性能监控。我当年在通信设备公司做底层驱动开发时,没少跟MPC826…

阅读更多
天地图、OpenStreetMap、ArcGIS Online,Web地图瓦片服务(WMTS/TMS/XYZ)到底怎么选?一个前端开发者的实战踩坑笔记
2026/6/14 10:57:47

天地图、OpenStreetMap、ArcGIS Online,Web地图瓦片服务(WMTS/TMS/XYZ)到底怎么选?一个前端开发者的实战踩坑笔记

天地图、OpenStreetMap与ArcGIS Online地图服务选型指南:前端开发避坑实战第一次在项目中集成第三方地图服务时,我被各种术语淹没了——WMTS、TMS、XYZ这些协议有什么区别?天地图的4490坐标系该如何处理?为什么OpenLayers加载的OS…

阅读更多
别再只用BERT了!用Transformers库的AutoModel,5分钟搞定文本相似度计算(附代码对比)
2026/6/14 0:57:30

别再只用BERT了!用Transformers库的AutoModel,5分钟搞定文本相似度计算(附代码对比)

超越BERT:用Transformers库高效实现文本相似度计算的三种实战方案在自然语言处理领域,文本相似度计算是信息检索、问答系统和推荐系统等应用的核心技术。传统方法如TF-IDF或Word2Vec已逐渐被基于Transformer的预训练模型所取代。Hugging Face的Transform…

阅读更多
Prompt Engineering:重构人机协作的工程化方法论
2026/6/14 0:57:30

Prompt Engineering:重构人机协作的工程化方法论

1. 项目概述:这不是“写提示词”,而是重构人机协作的底层逻辑“Prompt Engineering”这个词,这两年被讲得太多,也太轻飘。很多人把它理解成“给AI发指令的技巧”,甚至简化为“多加几个形容词”“换种说法再试一次”。我…

阅读更多
Anthropic提示层归零:模型即协议的工程实践
2026/6/14 0:57:30

Anthropic提示层归零:模型即协议的工程实践

1. 项目概述:这不是一次普通更新,而是一次架构级“蒸发”“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题一出来,我正在调试一个Claude调用链的终端前停了三秒。不是因为震惊,而是因为熟悉&…

阅读更多
别再只用BERT了!用Transformers库的AutoModel,5分钟搞定文本相似度计算(附代码对比)
2026/6/14 0:57:30

别再只用BERT了!用Transformers库的AutoModel,5分钟搞定文本相似度计算(附代码对比)

超越BERT:用Transformers库高效实现文本相似度计算的三种实战方案在自然语言处理领域,文本相似度计算是信息检索、问答系统和推荐系统等应用的核心技术。传统方法如TF-IDF或Word2Vec已逐渐被基于Transformer的预训练模型所取代。Hugging Face的Transform…

阅读更多
Prompt Engineering:重构人机协作的工程化方法论
2026/6/14 0:57:30

Prompt Engineering:重构人机协作的工程化方法论

1. 项目概述:这不是“写提示词”,而是重构人机协作的底层逻辑“Prompt Engineering”这个词,这两年被讲得太多,也太轻飘。很多人把它理解成“给AI发指令的技巧”,甚至简化为“多加几个形容词”“换种说法再试一次”。我…

阅读更多
Anthropic提示层归零:模型即协议的工程实践
2026/6/14 0:57:30

Anthropic提示层归零:模型即协议的工程实践

1. 项目概述:这不是一次普通更新,而是一次架构级“蒸发”“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题一出来,我正在调试一个Claude调用链的终端前停了三秒。不是因为震惊,而是因为熟悉&…

阅读更多
GIT修改用户名
2026/6/14 11:53:59

GIT修改用户名

在GIT中修改用户名可按以下步骤操作: 查看当前git的用户名,使用命令git config --list或git config user.name。修改git用户名,使用命令git config --global user.name "xxx(新的用户名)",将其中…

阅读更多
Win11Debloat:让你的Windows系统重获新生的终极优化工具
2026/6/13 15:45:46

Win11Debloat:让你的Windows系统重获新生的终极优化工具

Win11Debloat:让你的Windows系统重获新生的终极优化工具 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and …

阅读更多
技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践
2026/6/13 11:10:35

技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践

技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter m4s-converter是一个…

阅读更多