乐闻世界logo
搜索文章和话题

Puppeteer 如何实现页面交互和表单操作?有哪些常用的 API 和最佳实践?

2月19日 19:48

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'] });

刷新页面:

javascript
await page.reload(); await page.reload({ waitUntil: 'networkidle2' });

前进和后退:

javascript
await 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. 点击操作

基本点击:

javascript
await page.click('#button'); await page.click('.submit-btn');

带选项的点击:

javascript
await page.click('#button', { button: 'left', // 'left', 'right', 'middle' clickCount: 1, // 点击次数 delay: 100, // 点击延迟(毫秒) offset: { // 点击位置偏移 x: 10, y: 10 } });

双击:

javascript
await page.click('#button', { clickCount: 2 });

右键点击:

javascript
await page.click('#button', { button: 'right' });

等待元素可点击:

javascript
await page.waitForSelector('#button', { visible: true }); await page.click('#button');

4. 文本输入

基本输入:

javascript
await page.type('#input', 'Hello World');

带选项的输入:

javascript
await page.type('#input', 'Hello World', { delay: 100, // 每个字符的延迟(毫秒) clear: true // 输入前清空输入框 });

模拟真实打字速度:

javascript
await page.type('#input', 'Hello World', { delay: 50 });

清空输入框:

javascript
await page.click('#input'); await page.keyboard.down('Control'); await page.keyboard.press('A'); await page.keyboard.up('Control'); await page.keyboard.press('Backspace');

5. 键盘操作

基本按键:

javascript
await 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');

特殊键:

javascript
await 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. 鼠标操作

移动鼠标:

javascript
await page.mouse.move(100, 100); await page.mouse.move(100, 100, { steps: 10 }); // 平滑移动

点击鼠标:

javascript
await page.mouse.click(100, 100); await page.mouse.click(100, 100, { button: 'left', clickCount: 1 });

按下和释放鼠标:

javascript
await 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. 滚动操作

滚动到页面底部:

javascript
await page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight); });

滚动到特定元素:

javascript
await page.evaluate(() => { document.querySelector('#target').scrollIntoView(); });

平滑滚动:

javascript
await page.evaluate(() => { window.scrollTo({ top: 1000, behavior: 'smooth' }); });

滚动指定距离:

javascript
await page.evaluate(() => { window.scrollBy(0, 500); });

9. 等待元素

等待元素出现:

javascript
await page.waitForSelector('.result'); await page.waitForSelector('.result', { visible: true }); await page.waitForSelector('.result', { hidden: true });

等待 XPath:

javascript
await page.waitForXPath('//div[@class="result"]');

等待函数:

javascript
await page.waitForFunction(() => { return document.querySelectorAll('.item').length > 5; });

等待导航:

javascript
await Promise.all([ page.waitForNavigation(), page.click('#link') ]);

10. 获取元素信息

获取文本内容:

javascript
const text = await page.$eval('.title', el => el.textContent);

获取属性:

javascript
const href = await page.$eval('a', el => el.href); const id = await page.$eval('div', el => el.id);

获取多个元素信息:

javascript
const texts = await page.$$eval('.item', elements => { return elements.map(el => el.textContent); });

检查元素是否存在:

javascript
const exists = await page.$('.element') !== null;

检查元素是否可见:

javascript
const isVisible = await page.$eval('.element', el => { return el.offsetParent !== null; });

11. 实际应用场景

场景 1:登录表单填写

javascript
async 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:搜索功能测试

javascript
async 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:分页数据抓取

javascript
async 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:动态内容加载

javascript
async 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. 错误处理:

javascript
try { 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. 清理资源:

javascript
try { // 操作代码 } finally { await browser.close(); }
标签:Puppeteer