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();
2. 元素操作
// 点击元素
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)
);
3. 表单自动化
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();
}
4. 数据抓取
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;
}
5. 截图和 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'
}
});
6. 性能监控
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();
}
});
2. 模拟移动设备
const iPhone = puppeteer.devices['iPhone 12'];
await page.emulate(iPhone);
await page.goto('https://example.com');
3. 处理弹窗
// 处理 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;
4. Cookie 管理
// 设置 Cookie
await page.setCookie({
name: 'session',
value: 'abc123',
domain: 'example.com'
});
// 获取 Cookie
const cookies = await page.cookies();
// 删除 Cookie
await page.deleteCookie({ name: 'session' });
5. 执行 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();
}
}
2. 并发控制
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;
}
3. 资源优化
// 禁用不必要的资源
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 被封
- 妥善处理验证码和登录
- 定期更新浏览器版本
- 注意内存泄漏,及时关闭页面和浏览器
- 使用无头模式提高性能
- 保存日志和错误截图便于调试