如何使用Cypress爬取网站链接生成e2e测试待检测页面列表
问题核心原因
Cypress的测试运行分为两个独立阶段:
- 测试注册阶段:所有
describe、it块会在测试启动时同步执行完成,提前把所有待运行的测试用例注册到执行队列 - 测试执行阶段:按注册顺序执行每个测试用例内的Cypress异步命令
你原代码的while循环是在注册阶段执行的,此时队列中只有初始的1个链接,只会注册1个测试用例。后续执行阶段往队列新增的链接不会触发新的测试用例注册,自然不会被处理。
可行实现方案
采用递归逻辑替代预注册测试用例的方式,支持动态处理增长的链接队列:
describe('全站链接爬取', () => { // 注意补全链接的http/https协议,否则cy.visit会报错 const linksQueue = ['https://www.example.com/']; const seen = {}; // 存储最终生成的待测试页面列表 const testPageList = []; before(() => { // 用cy.session持久化登录态,避免多页面跳转后登录失效 cy.session('login-auth', () => { cy.login(email, password); }); }); // 递归处理队列中的链接 function handleNextLink() { // 队列处理完毕,输出结果 if (linksQueue.length === 0) { cy.log('爬取完成,待测试页面列表:', testPageList); // 可写入本地文件供后续测试使用 cy.writeFile('cypress/fixtures/test-pages.json', testPageList); return; } const currentLink = linksQueue.pop(); // 过滤无效链接:已处理过的、站外链接、静态资源链接、非页面跳转链接 if ( seen[currentLink] || !currentLink.startsWith(Cypress.config('baseUrl')) || currentLink.match(/\.(png|jpg|js|css|pdf|zip)$/i) || currentLink.startsWith('mailto:') || currentLink.startsWith('tel:') ) { handleNextLink(); return; } seen[currentLink] = true; testPageList.push(currentLink); it(`爬取页面:${currentLink}`, () => { // 配置failOnStatusCode避免404/500页面直接中断爬取 cy.visit(currentLink, { failOnStatusCode: false }); // 直接用Cypress原生选择器获取a标签,自带等待逻辑更稳定 cy.get('a').each(($a) => { const newLink = $a.prop('href'); if (newLink && !seen[newLink]) { linksQueue.unshift(newLink); } }).then(() => { // 当前页面链接处理完毕,递归处理下一个链接 handleNextLink(); }); }); } // 启动爬取逻辑 handleNextLink(); });
替代cy.window的实现方式
- 直接使用
cy.get('a'):Cypress原生元素选择命令,自带页面加载等待、元素渲染等待逻辑,不会出现页面未渲染完成就提前拿标签的问题,稳定性远高于手动从window取document - 使用
cy.document():如果需要直接操作document对象,可用cy.document().then(doc => { ... })替代cy.window的写法,更符合Cypress的命令链规范
优化注意点
- 可新增爬取深度限制,给每个链接关联层级标识,超过最大深度直接跳过,避免大型站点爬取时间过长
- 可新增域名白名单配置,支持爬取同集团下的多域名站点
- 可根据需求配置是否爬取hash路由的不同锚点,避免把同一个页面的不同锚点识别为多个页面
内容的提问来源于stack exchange,提问作者dearamerican
相关产品推荐
相关产品推荐

