启动/关闭浏览器、打开页面
// 启动浏览器
const browser = await puppeteer.launch({
// 关闭无头模式,方便我们看到这个无头浏览器执行的过程
// headless: false,
timeout: 30000, // 默认超时为30秒,设置为0则表示不设置超时
});
// 打开空白页面
const page = await browser.newPage();
// 进行交互
// ...
// 关闭浏览器
// await browser.close();
设置页面视窗大小
// 设置浏览器视窗
page.setViewport({
width: 1376,
height: 768,
});
输入网址
// 地址栏输入网页地址
await page.goto('https://google.com/', {
// 配置项
// waitUntil: 'networkidle', // 等待网络状态为空闲的时候才继续执行
});
保存网页为图片
await page.screenshot({
path: 'path/to/saved.png',
});
保存网页为 pdf
await page.pdf({
path: 'path/to/saved.pdf',
format: 'A4', // 保存尺寸
});
执行脚本
要获取打开的网页中的宿主环境,我们可以使用 Page.evaluate 方法:
// 获取视窗信息
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
console.log('视窗信息:', dimensions);
// 获取 html
// 获取上下文句柄
const htmlHandle = await page.$('html');
// 执行计算
const html = await page.evaluate(body => body.outerHTML, htmlHandle);
// 销毁句柄
await htmlHandle.dispose();
console.log('html:', html);
Page.$ 可以理解为我们常用的 document.querySelector, 而 Page.$$ 则对应 document.querySelectorAll。
自动提交表单
// 地址栏输入网页地址
await page.goto('https://google.com/', {
waitUntil: 'networkidle', // 等待网络状态为空闲的时候才继续执行
});
// 聚焦搜索框
// await page.click('#lst-ib');
await page.focus('#lst-ib');
// 输入搜索关键字
await page.type('辣子鸡', {
delay: 1000, // 控制 keypress 也就是每个字母输入的间隔
});
// 回车
await page.press('Enter');
抓取单页应用: 模拟饿了么外卖下单
关键代码
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone6 = devices['iPhone 6'];
console.log('启动浏览器');
const browser = await puppeteer.launch();
console.log('打开页面');
const page = await browser.newPage();
// 模拟移动端设备
await page.emulate(iPhone6);
console.log('地址栏输入网页地址');
await page.goto(url);
console.log('等待页面准备好');
await page.waitForSelector('.search-wrapper .search');
console.log('点击搜索框');
await page.tap('.search-wrapper .search');
await page.type('麦当劳', {
delay: 200, // 每个字母之间输入的间隔
});
console.log('回车开始搜索');
await page.tap('button');
console.log('等待搜素结果渲染出来');
await page.waitForSelector('[class^="index-container"]');
console.log('找到搜索到的第一家外卖店!');
await page.tap('[class^="index-container"]');
console.log('等待菜单渲染出来');
await page.waitForSelector('[class^="fooddetails-food-panel"]');
console.log('直接选一个菜品吧');
await page.tap('[class^="fooddetails-cart-button"]');
// console.log('===为了看清楚,傲娇地等两秒===');
await page.waitFor(2000);
await page.tap('[class^=submit-btn-submitbutton]');
// 关闭浏览器
await browser.close();
关键步骤是:
- 加载页面
- 等待需要点击的 DOM 渲染出来后点击
- 继续等待下一步需要点击的 DOM 渲染出来再点击
关键的几个指令:
- page.tap(或 page.click) 为点击
- page.waitForSelector 意思是等待指定元素出现在网页中,如果已经出现了,则立即继续执行下去, 后面跟的参数为 selector 选择器,与我们常用的document.querySelector 接收的参数一致
- page.waitFor 后面可以传入 selector 选择器、function 函数或 timeout 毫秒时间,如 page.waitFor(2000) 指等待2秒再继续执行,例子中用这个函数暂停操作主要是为了演示
以上几个指令都可接受一个 selector 选择器作为参数,这里额外介绍几个方法:
- page.$(selector) 与我们常用的 document.querySelector(selector) 一致,返回的是一个 ElementHandle 元素句柄
- page.$$(selector) 与我们常用的 document.querySelectorAll(selector) 一致,返回的是一个数组
在有头浏览器上下文中,我们选择一个元素的方法是:
const body = document.querySelector('body');
const bodyInnerHTML = body.innerHTML;
console.log('bodyInnerHTML: ', bodyInnerHTML);
而在无头浏览器里,我们首先需要获取一个句柄,通过句柄获取到环境中的信息后,销毁这个句柄。
// 获取 html
// 获取上下文句柄
const bodyHandle = await page.$('body');
// 执行计算
const bodyInnerHTML = await page.evaluate(dom => dom.innerHTML, bodyHandle);
// 销毁句柄
await bodyHandle.dispose();
console.log('bodyInnerHTML:', bodyInnerHTML);
除此之外,还可以使用 page.$eval:
const bodyInnerHTML = await page.$eval('body', dom => dom.innerHTML);
console.log('bodyInnerHTML: ', bodyInnerHTML);
page.evaluate 意为在浏览器环境执行脚本,可传入第二个参数作为句柄,而 page.$eval 则针对选中的一个 DOM 元素执行操作。