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





权限与依赖数据

sessionStorage

localStorage

headers请求头

经典案例

Last Updated:
Contributors: 709992523