Puppeteer爬虫进阶:手把手教你拦截并Mock网络请求,打造本地测试数据

Puppeteer爬虫进阶:手把手教你拦截并Mock网络请求,打造本地测试数据 Puppeteer请求拦截与Mock实战构建前端开发的本地数据沙盒当后端API尚未就绪或频繁变动时前端开发往往陷入被动等待的困境。本文将揭示如何利用Puppeteer的请求拦截能力打造一个完全可控的本地数据沙盒环境。不同于基础教程我们聚焦实战场景下的高级技巧包括动态路由匹配、性能优化和复杂响应构造。1. 为什么需要请求拦截与Mock现代前端开发中前后端分离架构已成为主流。但这也带来了新的挑战当后端接口延迟交付或频繁变更时前端开发进度常被阻塞。传统解决方案如静态JSON文件或本地服务器存在局限性静态文件无法模拟网络延迟、错误状态等真实场景Mock服务器需要额外维护路由配置响应不够灵活第三方工具往往需要复杂配置学习成本高Puppeteer的setRequestInterception能力恰好填补了这一空白。通过直接拦截浏览器层面的网络请求我们可以动态修改请求参数和头信息返回任意构造的响应数据模拟各种网络状态如404、500错误完全绕过实际后端服务// 基础拦截示例 await page.setRequestInterception(true); page.on(request, interceptedRequest { if (interceptedRequest.url().includes(/api/data)) { interceptedRequest.respond({ status: 200, contentType: application/json, body: JSON.stringify({ mock: data }) }); } else { interceptedRequest.continue(); } });2. 构建智能拦截系统2.1 动态路由匹配策略简单的URL字符串匹配在复杂场景下显得力不从心。我们需要更强大的路由识别机制匹配方式实现方法适用场景精确匹配url target固定API端点包含匹配url.includes(keyword)通用路径片段正则表达式pattern.test(url)复杂URL模式参数解析URLSearchParams解析需要校验查询参数时// 高级路由匹配实现 const routeRules [ { test: /\/api\/v1\/users\/\d/, handler: (req) ({ status: 200, body: mockUserProfile(req.url.match(/\d/)[0]) }) }, { test: (url) new URL(url).pathname /api/search, handler: (req) { const params new URLSearchParams(new URL(req.url()).search); return mockSearchResults(params.get(keyword)) } } ]; page.on(request, (req) { const rule routeRules.find(r typeof r.test function ? r.test(req.url()) : r.test.test(req.url()) ); rule ? req.respond(rule.handler(req)) : req.continue(); });2.2 响应构造的艺术真实的Mock数据需要考虑多种维度数据结构模板// 使用工厂函数生成动态数据 const mockProduct (id) ({ id, name: faker.commerce.productName(), price: faker.commerce.price(), stock: Math.floor(Math.random() * 100), updatedAt: new Date().toISOString() });状态码模拟// 错误状态模拟 const simulateError () ({ status: Math.random() 0.8 ? 500 : 200, headers: { Retry-After: 30 }, body: JSON.stringify({ error: Service unavailable }) });延迟响应控制// 网络延迟模拟 const delayedResponse (response, delay 300) new Promise(resolve setTimeout(() resolve(response), delay) );3. 性能优化与调试技巧3.1 选择性拦截策略盲目拦截所有请求会导致性能下降。最佳实践是白名单模式只拦截目标API相关请求资源过滤忽略图片、字体等静态资源缓存复用对相同请求返回缓存结果// 性能优化拦截方案 const apiCache new Map(); page.on(request, async (req) { const url req.url(); if (!url.includes(/api/)) { return req.continue(); } if (apiCache.has(url)) { return req.respond(apiCache.get(url)); } const mockData generateMockResponse(req); apiCache.set(url, mockData); req.respond(mockData); });3.2 调试工具集成结合Puppeteer的调试能力可以极大提升开发效率请求日志记录所有拦截的请求详情差异对比比较Mock数据与实际API响应流量统计分析请求频率和性能指标// 调试日志实现 page.on(request, req { console.log([${req.method()}] ${req.url()}); console.log(Headers:, req.headers()); console.log(Post Data:, req.postData()); }); page.on(response, async res { console.log([${res.status()}] ${res.url()}); if (res.request().resourceType() xhr) { console.log(Response:, await res.text()); } });4. 进阶应用场景4.1 认证状态模拟对于需要认证的接口可以模拟完整的登录流程// JWT认证模拟 const simulateAuth (req) { const token req.headers()[authorization]?.split( )[1]; if (!token || !validateToken(token)) { return { status: 401, body: JSON.stringify({ error: Unauthorized }) }; } return generateAuthResponse(token); };4.2 GraphQL接口处理针对GraphQL的特殊需求需要解析请求内容page.on(request, async (req) { if (req.url().includes(/graphql)) { const { query, variables } JSON.parse(req.postData()); if (query.includes(query GetUser)) { return req.respond({ status: 200, contentType: application/json, body: JSON.stringify({ data: mockGraphQLUser(variables.id) }) }); } } req.continue(); });4.3 流量控制与限流模拟API限流场景const rateLimiter new Map(); page.on(request, (req) { const ip req.headers()[x-forwarded-for] || 127.0.0.1; const count rateLimiter.get(ip) || 0; if (count 100) { return req.respond({ status: 429, body: Too Many Requests }); } rateLimiter.set(ip, count 1); setTimeout(() { const current rateLimiter.get(ip); if (current) rateLimiter.set(ip, current - 1); }, 60000); req.continue(); });在实际项目中这种技术方案帮助我们团队将前端开发对后端的依赖度降低了70%特别是在早期原型阶段开发者可以完全自主控制数据返回结构和响应时间。一个典型的应用场景是模拟分页加载效果——通过控制延迟时间和分页数据我们能够完美复现真实环境下的滚动加载体验而无需等待后端实现。