Puppeteer 提供了丰富的页面交互和表单操作功能,可以模拟用户的真实操作行为,这对于自动化测试和网页爬虫非常重要。
1. 基本页面操作
导航到页面:
javascript// 基本导航 await page.goto('https://example.com'); // 等待网络空闲 await page.goto('https://example.com', { waitUntil: 'networkidle2' }); // 设置超时时间 await page.goto('https://example.com', { timeout: 30000 }); // 等待特定条件 await page.goto('https://example.com', { waitUntil: ['load', 'domcontentloaded'] });
刷新页面:
javascriptawait page.reload(); await page.reload({ waitUntil: 'networkidle2' });
前进和后退:
javascriptawait page.goBack(); await page.goForward();
2. 元素选择
Puppeteer 支持多种选择器方式。
使用 $ 选择单个元素:
javascript// 通过 CSS 选择器 const element = await page.$('#my-id'); const element = await page.$('.my-class'); const element = await page.$('div > p'); // 通过 XPath const element = await page.$x('//div[@class="my-class"]');
使用 $$ 选择多个元素:
javascript// 选择所有匹配的元素 const elements = await page.$$('.item'); console.log(elements.length); // 元素数量 // 遍历元素 for (const element of elements) { const text = await element.evaluate(el => el.textContent); console.log(text); }
使用 $$eval 批量获取数据:
javascript// 获取所有元素的文本 const texts = await page.$$eval('.item', elements => { return elements.map(el => el.textContent); }); // 获取所有元素的属性 const hrefs = await page.$$eval('a', elements => { return elements.map(el => el.href); });
3. 点击操作
基本点击:
javascriptawait page.click('#button'); await page.click('.submit-btn');
带选项的点击:
javascriptawait page.click('#button', { button: 'left', // 'left', 'right', 'middle' clickCount: 1, // 点击次数 delay: 100, // 点击延迟(毫秒) offset: { // 点击位置偏移 x: 10, y: 10 } });
双击:
javascriptawait page.click('#button', { clickCount: 2 });
右键点击:
javascriptawait page.click('#button', { button: 'right' });
等待元素可点击:
javascriptawait page.waitForSelector('#button', { visible: true }); await page.click('#button');
4. 文本输入
基本输入:
javascriptawait page.type('#input', 'Hello World');
带选项的输入:
javascriptawait page.type('#input', 'Hello World', { delay: 100, // 每个字符的延迟(毫秒) clear: true // 输入前清空输入框 });
模拟真实打字速度:
javascriptawait page.type('#input', 'Hello World', { delay: 50 });
清空输入框:
javascriptawait page.click('#input'); await page.keyboard.down('Control'); await page.keyboard.press('A'); await page.keyboard.up('Control'); await page.keyboard.press('Backspace');
5. 键盘操作
基本按键:
javascriptawait page.keyboard.press('Enter'); await page.keyboard.press('Tab'); await page.keyboard.press('Escape'); await page.keyboard.press('Backspace');
组合键:
javascript// Ctrl+C await page.keyboard.down('Control'); await page.keyboard.press('C'); await page.keyboard.up('Control'); // Ctrl+A (全选) await page.keyboard.down('Control'); await page.keyboard.press('A'); await page.keyboard.up('Control'); // Ctrl+V (粘贴) await page.keyboard.down('Control'); await page.keyboard.press('V'); await page.keyboard.up('Control');
特殊键:
javascriptawait page.keyboard.press('ArrowUp'); await page.keyboard.press('ArrowDown'); await page.keyboard.press('ArrowLeft'); await page.keyboard.press('ArrowRight'); await page.keyboard.press('PageUp'); await page.keyboard.press('PageDown'); await page.keyboard.press('Home'); await page.keyboard.press('End');
6. 鼠标操作
移动鼠标:
javascriptawait page.mouse.move(100, 100); await page.mouse.move(100, 100, { steps: 10 }); // 平滑移动
点击鼠标:
javascriptawait page.mouse.click(100, 100); await page.mouse.click(100, 100, { button: 'left', clickCount: 1 });
按下和释放鼠标:
javascriptawait page.mouse.down(); await page.mouse.up(); // 拖拽操作 await page.mouse.down({ x: 100, y: 100 }); await page.mouse.move(200, 200, { steps: 10 }); await page.mouse.up();
7. 表单操作
填写表单:
javascript// 文本输入 await page.type('#name', 'John Doe'); await page.type('#email', 'john@example.com'); // 选择下拉框 await page.selectOption('#country', 'CN'); await page.selectOption('#country', ['CN', 'US']); // 多选 // 复选框 await page.click('#checkbox'); const isChecked = await page.$eval('#checkbox', el => el.checked); // 单选框 await page.click('#radio-male'); // 文件上传 await page.setInputFiles('#file-upload', '/path/to/file.pdf'); await page.setInputFiles('#file-upload', ['/file1.pdf', '/file2.pdf']);
提交表单:
javascript// 点击提交按钮 await page.click('#submit-button'); // 使用表单提交 await page.evaluate(() => { document.querySelector('form').submit(); });
8. 滚动操作
滚动到页面底部:
javascriptawait page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight); });
滚动到特定元素:
javascriptawait page.evaluate(() => { document.querySelector('#target').scrollIntoView(); });
平滑滚动:
javascriptawait page.evaluate(() => { window.scrollTo({ top: 1000, behavior: 'smooth' }); });
滚动指定距离:
javascriptawait page.evaluate(() => { window.scrollBy(0, 500); });
9. 等待元素
等待元素出现:
javascriptawait page.waitForSelector('.result'); await page.waitForSelector('.result', { visible: true }); await page.waitForSelector('.result', { hidden: true });
等待 XPath:
javascriptawait page.waitForXPath('//div[@class="result"]');
等待函数:
javascriptawait page.waitForFunction(() => { return document.querySelectorAll('.item').length > 5; });
等待导航:
javascriptawait Promise.all([ page.waitForNavigation(), page.click('#link') ]);
10. 获取元素信息
获取文本内容:
javascriptconst text = await page.$eval('.title', el => el.textContent);
获取属性:
javascriptconst href = await page.$eval('a', el => el.href); const id = await page.$eval('div', el => el.id);
获取多个元素信息:
javascriptconst texts = await page.$$eval('.item', elements => { return elements.map(el => el.textContent); });
检查元素是否存在:
javascriptconst exists = await page.$('.element') !== null;
检查元素是否可见:
javascriptconst isVisible = await page.$eval('.element', el => { return el.offsetParent !== null; });
11. 实际应用场景
场景 1:登录表单填写
javascriptasync function login(url, username, password) { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url); // 填写登录表单 await page.type('#username', username); await page.type('#password', password); // 点击登录按钮 await Promise.all([ page.waitForNavigation(), page.click('#login-button') ]); // 验证登录成功 const isLoggedIn = await page.$('.user-profile') !== null; await browser.close(); return isLoggedIn; } login('https://example.com/login', 'user@example.com', 'password');
场景 2:搜索功能测试
javascriptasync function testSearch(url, query) { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url); // 输入搜索关键词 await page.type('#search-input', query); // 提交搜索 await Promise.all([ page.waitForNavigation(), page.keyboard.press('Enter') ]); // 等待搜索结果 await page.waitForSelector('.search-result'); // 获取结果数量 const resultCount = await page.$$eval('.search-result', results => { return results.length; }); console.log(`Found ${resultCount} results for "${query}"`); await browser.close(); return resultCount; } testSearch('https://example.com', 'puppeteer');
场景 3:分页数据抓取
javascriptasync function scrapePaginatedData(url) { const browser = await puppeteer.launch(); const page = await browser.newPage(); const allData = []; let hasNextPage = true; let pageNum = 1; while (hasNextPage) { await page.goto(`${url}?page=${pageNum}`); await page.waitForSelector('.item'); // 抓取当前页数据 const pageData = await page.$$eval('.item', items => { return items.map(item => ({ title: item.querySelector('.title').textContent, price: item.querySelector('.price').textContent })); }); allData.push(...pageData); console.log(`Scraped page ${pageNum}: ${pageData.length} items`); // 检查是否有下一页 hasNextPage = await page.$('.next-page:not(.disabled)') !== null; pageNum++; } await browser.close(); return allData; } scrapePaginatedData('https://example.com/products');
场景 4:动态内容加载
javascriptasync function scrapeDynamicContent(url) { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url); // 等待初始内容加载 await page.waitForSelector('.content'); // 滚动加载更多内容 while (true) { // 滚动到底部 await page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight); }); // 等待新内容加载 try { await page.waitForSelector('.new-content', { timeout: 3000 }); } catch (error) { break; // 没有新内容了 } } // 获取所有内容 const allContent = await page.$$eval('.content-item', items => { return items.map(item => item.textContent); }); await browser.close(); return allContent; } scrapeDynamicContent('https://example.com/infinite-scroll');
12. 最佳实践
1. 使用等待机制:
javascript// 好的做法 await page.waitForSelector('.button', { visible: true }); await page.click('.button'); // 不好的做法 await page.click('.button'); // 可能失败
2. 处理动态内容:
javascript// 等待网络空闲 await page.goto(url, { waitUntil: 'networkidle2' }); // 等待特定元素 await page.waitForSelector('.loaded-content');
3. 错误处理:
javascripttry { await page.click('#button'); } catch (error) { console.error('Click failed:', error); // 重试逻辑 }
4. 性能优化:
javascript// 禁用不必要的资源 await page.setRequestInterception(true); page.on('request', (request) => { if (['image', 'font', 'media'].includes(request.resourceType())) { request.abort(); } else { request.continue(); } });
5. 清理资源:
javascripttry { // 操作代码 } finally { await browser.close(); }