Puppeteer
重要通知
。
基本概况
Puppeteer是一个Node.js库,它提供了一个高级API来控制 Chrome/Chromium over the DevTools Protocol。
安装配置
代码示例
const puppeteer = require('puppeteer');
module.exports = async function () {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'screenshot.png'});
await browser.close();
}
基础API
浏览器 Browser
- 启动具有给定参数和选项(如果指定)的浏览器实例
const browser = await puppeteer.launch({
headless: true, // 无头模式, boolean | 'new', true-启用无头模式 false-启用有头模式 'new'-Chrome 112 推出的一种新的无头模式
args: [ // , string[],
// 传递给浏览器实例的参数
'--no-proxy-server',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
],
debuggingPort: , // , number,
devtools: false, // , boolean,
userDataDir: , // , string,
});
browser.close();
键盘 Keyboard
鼠标 Mouse
触摸屏 Touchscreen
框架 Frame
延时、等待时间与超时
// 等待
page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]]) # 延迟下一步操作
page.waitForFileChooser([options])
page.waitForFunction(pageFunction[, options[, ...args]])
page.waitForNavigation([options]) # 延迟跳转时间
page.waitForRequest(urlOrPredicate[, options])
page.waitForResponse(urlOrPredicate[, options])
page.waitForSelector(selector[, options])
page.waitForXPath(xpath[, options])
// page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])
selector: 选择器, 检测DOM元素加载
Function: 函数, 等待函数执行完成
Timeout: 延迟时间, 延迟等待时间
网页 Page
const page = await browser.newPage();
page.viewport({
width: ,
height: ,
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
isLandScape: false
});
page.close();
URL
const url = "";
page.goto(url, {
timeout: 10000, // 页面超时容错时间
waitUntil: [ // 等待条件
'load', // window.onload 事件被触发时继续。
'domcontentloaded', // Domcontentloaded 事件触发时继续。
'networkidle0', // 在 500ms 内没有网络连接时(全部的request结束)则继续。
'networkidle2' // 500ms 内有不超过 2 个网络连接时就算成功(还有两个以下的request)则继续。
]
});
导出 PDF
await page.pdf({
path: STATIC_PATH + `${Date.now()}.pdf`,
width: 1920,
printBackground: true,
displayHeaderFooter: true,
format: 'A4'
});
导出图片
- 截图整个页面范围
const image = page.screenshot({
captureBeyondViewport: true,
clip: {
width: ,
height: ,
scale: 1,
x: ,
y:
},
encoding: 'binary', // 'base64' | 'binary'
fromSurface: false,
fullPage: false,
omitBackground: false, // 隐藏网页背景, 即transparent
optimizeForSpeed: ,
path: , // 保存图片磁盘路径,如果不存在,则返回流数据, '/home/meta/server/oss/'
quality: 100,
type: 'png', // 图片格式, 默认为'png', 取值'png' | 'jpeg' | 'webp'
});
// 方案一:返回图片数据结构
const base64Image = await image.toString('base64');
// 方案二:返回图片数据结构
ctx.body = image;
- 截图指定DOM结构范围
/**
* 图片生成模型
* @param {*} data 数据
* @param {*} headers 请求头
* @returns
*/
async function imageModel(data = {}, headers = {}) {
const { userInfo, recordInfo, activityID, activityDetail, homeLink, shareLink } = data || {};
const { sessionid: session_id, token } = headers || {};
if (
!userInfo ||
!recordInfo ||
!activityID ||
!activityDetail ||
!homeLink ||
!shareLink
) {
return null;
}
if (
!session_id ||
!token
) {
return null;
}
// 渲染网页
const page = await browser.newPage();
await page.goto(homeLink, { waitUntil: 'networkidle2' });
// 添加 localStorage 缓存
await page.evaluate((token, session_id, activityID, activityDetail = {}, userInfo = {}, recordInfo = {}) => {
localStorage.clear();
localStorage.setItem('token', token);
localStorage.setItem('session_id', session_id);
localStorage.setItem('user_info', JSON.stringify(userInfo));
localStorage.setItem('activity_id', activityID);
localStorage.setItem('activity_detail', JSON.stringify(activityDetail));
localStorage.setItem('record_detail', JSON.stringify(recordInfo));
}, token, session_id, activityID, activityDetail, userInfo, recordInfo);
// 基于以上操作实现的localStorage缓存再次加载网页
await page.goto(shareLink, { waitUntil: 'networkidle2', timeout: 600000 });
// 移除 蒙层
const masklayer = "div.van-overlay.vant-dialog-mask";
await page.waitForSelector(masklayer);
const deleteMask = await page.$(masklayer);
await page.evaluate((node) => node.remove(), deleteMask);
// 移除 "打卡成功"弹窗
const modallayer = "div.van-dialog.component-failed-dialog";
await page.waitForSelector(modallayer);
const deleteModal = await page.$(modallayer);
await page.evaluate((node) => node.remove(), deleteModal);
const scrollViewClsName = "body > div#app > section > div.scroll-container > div.scroll-view";
await page.waitForSelector(scrollViewClsName);
const scrollViewDOM = await page.$(scrollViewClsName);
// 有效视口范围 截屏
const options = { encoding: 'binary' };
const image = await scrollViewDOM.screenshot(options);
return image;
}
DOM 操作
选择器
const element = await page.waitForSelector('div > .class-name');
await element.click();
await element.dispose();
- page.$(selector):此方法是page.mainFrame().$(selector) 的简写,在页面内执行 document.querySelector。如果没有元素匹配指定选择器,返回值是 null。
- page.$$(selector):此方法是page.mainFrame().$$(selector) 的简写。在页面内执行 document.querySelectorAll。如果没有元素匹配指定选择器,返回值是 []。
- page.$$eval(selector, pageFunction[, ...args]):此方法在页面内执行 Array.from(document.querySelectorAll(selector)),然后把匹配到的元素数组作为第一个参数传给 pageFunction。
- page.$eval(selector, pageFunction[, ...args]):
执行JS
Puppeteer 允许在页面上下文中执行 JavaScript 函数。
const three = await page.evaluate(() => {
return 1 + 2;
});
console.log(three);
- page.evaluate 传参
const result = await page.evaluate((name, age) => {
return {name, age};
}, 'Eshen', 19);
console.log(result);
拦截请求 HTTPRequest
拦截响应 HTTPResponse