Chrome 浏览器自动化 功能说明
此技能专门用于 Chrome 浏览器自动化,包括:
网页自动化操作 E2E 测试 网页截图和 PDF 生成 表单自动填充 动态网页数据抓取 性能测试和监控 使用场景 "自动化登录网站并抓取数据" "批量截取网页截图" "自动化测试购物流程" "监控网站性能指标" "自动填写表单并提交" "生成网页 PDF 报告" 技术栈 自动化工具 Puppeteer:Chrome DevTools Protocol Playwright:跨浏览器自动化 Selenium:经典自动化框架 Cypress:现代 E2E 测试框架 辅助工具 puppeteer-extra:Puppeteer 插件系统 puppeteer-stealth:反检测插件 chrome-launcher:Chrome 启动器 核心功能 1. 页面导航 import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: false, // 显示浏览器 slowMo: 50 // 减慢操作速度 });
const page = await browser.newPage();
// 导航到页面 await page.goto('https://example.com', { waitUntil: 'networkidle2' // 等待网络空闲 });
// 前进后退 await page.goBack(); await page.goForward(); await page.reload();
await browser.close();
- 元素操作 // 点击元素 await page.click('#submit-button');
// 输入文本 await page.type('#username', 'user@example.com'); await page.type('#password', 'password123');
// 选择下拉框 await page.select('#country', 'CN');
// 上传文件 const fileInput = await page.$('input[type="file"]'); await fileInput.uploadFile('/path/to/file.pdf');
// 等待元素 await page.waitForSelector('.result', { timeout: 5000 });
// 获取元素文本 const text = await page.$eval('.title', el => el.textContent);
// 获取多个元素 const items = await page.$$eval('.item', elements => elements.map(el => el.textContent) );
- 表单自动化 async function fillForm(page: Page) { // 填写文本输入框 await page.type('#name', '张三'); await page.type('#email', 'zhangsan@example.com'); await page.type('#phone', '13800138000');
// 选择单选按钮 await page.click('input[name="gender"][value="male"]');
// 选择复选框 await page.click('#agree-terms'); await page.click('#subscribe-newsletter');
// 选择下拉框 await page.select('#city', 'beijing');
// 填写日期 await page.type('#birthday', '1990-01-01');
// 填写文本域 await page.type('#message', '这是一条测试消息');
// 提交表单 await page.click('button[type="submit"]');
// 等待提交完成 await page.waitForNavigation(); }
- 数据抓取 async function scrapeData(url: string) { const browser = await puppeteer.launch(); const page = await browser.newPage();
await page.goto(url);
// 等待内容加载 await page.waitForSelector('.product-list');
// 抓取数据 const products = await page.$$eval('.product-item', items => items.map(item => ({ title: item.querySelector('.title')?.textContent, price: item.querySelector('.price')?.textContent, image: item.querySelector('img')?.src, link: item.querySelector('a')?.href })) );
await browser.close(); return products; }
- 截图和 PDF // 全页截图 await page.screenshot({ path: 'screenshot.png', fullPage: true });
// 元素截图 const element = await page.$('.chart'); await element.screenshot({ path: 'chart.png' });
// 生成 PDF await page.pdf({ path: 'page.pdf', format: 'A4', printBackground: true, margin: { top: '20px', right: '20px', bottom: '20px', left: '20px' } });
- 性能监控 async function measurePerformance(url: string) { const browser = await puppeteer.launch(); const page = await browser.newPage();
// 开始性能追踪 await page.tracing.start({ path: 'trace.json' });
await page.goto(url, { waitUntil: 'networkidle2' });
// 停止追踪 await page.tracing.stop();
// 获取性能指标 const metrics = await page.metrics(); console.log('性能指标:', metrics);
// 获取 Performance API 数据 const performanceData = await page.evaluate(() => { const timing = performance.timing; return { loadTime: timing.loadEventEnd - timing.navigationStart, domReady: timing.domContentLoadedEventEnd - timing.navigationStart, firstPaint: performance.getEntriesByType('paint')[0]?.startTime }; });
await browser.close(); return performanceData; }
高级功能 1. 拦截和修改请求 await page.setRequestInterception(true);
page.on('request', request => { // 阻止图片加载 if (request.resourceType() === 'image') { request.abort(); } // 修改请求头 else if (request.url().includes('api')) { request.continue({ headers: { ...request.headers(), 'Authorization': 'Bearer token' } }); } else { request.continue(); } });
- 模拟移动设备 const iPhone = puppeteer.devices['iPhone 12'];
await page.emulate(iPhone); await page.goto('https://example.com');
- 处理弹窗 // 处理 alert/confirm/prompt page.on('dialog', async dialog => { console.log(dialog.message()); await dialog.accept(); // 或 dialog.dismiss() });
// 处理新窗口 const newPagePromise = new Promise(resolve => browser.once('targetcreated', target => resolve(target.page())) );
await page.click('a[target="_blank"]'); const newPage = await newPagePromise;
- Cookie 管理 // 设置 Cookie await page.setCookie({ name: 'session', value: 'abc123', domain: 'example.com' });
// 获取 Cookie const cookies = await page.cookies();
// 删除 Cookie await page.deleteCookie({ name: 'session' });
- 执行 JavaScript // 在页面上下文中执行代码 const result = await page.evaluate(() => { return document.title; });
// 传递参数 const sum = await page.evaluate((a, b) => { return a + b; }, 5, 3);
// 暴露函数给页面 await page.exposeFunction('md5', (text: string) => { return crypto.createHash('md5').update(text).digest('hex'); });
E2E 测试示例 Playwright 测试 import { test, expect } from '@playwright/test';
test.describe('登录功能', () => { test('成功登录', async ({ page }) => { await page.goto('https://example.com/login');
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('https://example.com/dashboard');
await expect(page.locator('h1')).toContainText('欢迎');
});
test('登录失败提示', async ({ page }) => { await page.goto('https://example.com/login');
await page.fill('#email', 'wrong@example.com');
await page.fill('#password', 'wrongpass');
await page.click('button[type="submit"]');
await expect(page.locator('.error')).toBeVisible();
await expect(page.locator('.error')).toContainText('用户名或密码错误');
}); });
Cypress 测试 describe('购物车功能', () => { beforeEach(() => { cy.visit('/products'); });
it('添加商品到购物车', () => { cy.get('.product-item').first().within(() => { cy.get('.add-to-cart').click(); });
cy.get('.cart-badge').should('contain', '1');
});
it('从购物车删除商品', () => { cy.get('.cart-icon').click(); cy.get('.cart-item').first().within(() => { cy.get('.remove-button').click(); });
cy.get('.cart-empty').should('be.visible');
}); });
反检测技术 Puppeteer Stealth import puppeteer from 'puppeteer-extra'; import StealthPlugin from 'puppeteer-extra-plugin-stealth';
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled' ] });
const page = await browser.newPage();
// 设置真实的 User-Agent await page.setUserAgent( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' );
// 设置视口大小 await page.setViewport({ width: 1920, height: 1080 });
// 隐藏 webdriver 标识 await page.evaluateOnNewDocument(() => { Object.defineProperty(navigator, 'webdriver', { get: () => false }); });
最佳实践 1. 错误处理 async function robustScrape(url: string) { const browser = await puppeteer.launch(); const page = await browser.newPage();
try { await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
const data = await page.evaluate(() => {
// 数据提取逻辑
});
return data;
} catch (error) { console.error('抓取失败:', error); // 截图保存错误现场 await page.screenshot({ path: 'error.png' }); throw error; } finally { await browser.close(); } }
- 并发控制 import pLimit from 'p-limit';
async function scrapeMultiplePages(urls: string[]) { const browser = await puppeteer.launch(); const limit = pLimit(5); // 最多 5 个并发
const results = await Promise.all( urls.map(url => limit(async () => { const page = await browser.newPage(); try { await page.goto(url); return await page.evaluate(() => { // 提取数据 }); } finally { await page.close(); } }) ) );
await browser.close(); return results; }
- 资源优化 // 禁用不必要的资源 await page.setRequestInterception(true); page.on('request', request => { const resourceType = request.resourceType(); if (['image', 'stylesheet', 'font'].includes(resourceType)) { request.abort(); } else { request.continue(); } });
// 设置超时 page.setDefaultTimeout(10000); page.setDefaultNavigationTimeout(30000);
注意事项 遵守网站的 robots.txt 和服务条款 控制请求频率,避免对服务器造成压力 使用代理池避免 IP 被封 妥善处理验证码和登录 定期更新浏览器版本 注意内存泄漏,及时关闭页面和浏览器 使用无头模式提高性能 保存日志和错误截图便于调试