灏天阁

从零开始学习 puppeteer(一)

· Yin灏

Puppeteer 通常用在自动化测试和爬取页面,提供丰富 api 来控制 headless Chrome 或者 Chromium。

安装

npm 包比较大,推荐全局安装。

npm i puppeteer
# or using yarn
yarn add puppeteer
# or using pnpm
pnpm i puppeteer

使用

环境配置

pnpm init -y
pnpm i puppeteer
touch demo.js

刚开始先通过 node demo.js 运行项目。

官方案例

  • puppeteer.launch 创建 Browser 实例,类似打开浏览器

  • browser.newPage 创建 Page 实例,类似浏览器中新开页面

  • browser.close() 关闭浏览器。

const puppeteer = require("puppeteer");

(async () => {
  // 1. 创建了Browser实例,类似打开浏览器
  const browser = await puppeteer.launch({
    headless: "new",
  });
  console.log("打开浏览器");
  // 2. 创建了Page实例,类似浏览器中新开页面
  const page = await browser.newPage();
  console.log("打开页面");
  // 导航到要访问的页面
  await page.goto("https://www.baidu.com");

  await page.screenshot({ path: "example.png" });

  console.log("success");

  // 在页面中执行脚本 Get the "viewport" of the page, as reported by the page.
  const dimensions = await page.evaluate(() => {
    return {
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
      deviceScaleFactor: window.devicePixelRatio,
    };
  });

  console.log("Dimensions:", dimensions);
  // 设置可视区域
  await page.setViewport({
    width: 1420,
    height: 1000,
  });
  // 全屏截取
  await page.screenshot({
    path: "example1.png",
    fullPage: true,
  });

  // waitUntil 来配置什么时候导航结束 "domcontentloaded"、"networkidle0"、"networkidle2"
  await page.goto("https://news.ycombinator.com", {
    waitUntil: "networkidle2",
  });
  await page.pdf({ path: "hn.pdf", format: "A4" });
  console.log("success pdf");
  // 3. 关闭浏览器
  await browser.close();
})();

page.goto 用于导航到新页面,waitUntil 是指定一个等待条件,告诉 puppeteer 合适导航完成。默认取值 load, 还有 documentloaded 等待页面 DOMcontentLoaded 事件触发。

page.evaluate 功能类似 eval,在浏览器中执行脚本,结果返回程序。

page.$(selector) 就是 document.querySelector,而 page.$$(selector) 则是 document.querySelectorAll.

这个 case 仅仅是整合了官方最简单的三个案例。运行一遍,有个概念。

拓展 - 滚动截屏

很多网站数据是懒加载的,如果想要把所有的数据获取到,则需要一些相关的元素操作,比如滚动。

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({
    headless: "new",
  });
  console.log("打开浏览器");
  const page = await browser.newPage();
  console.log("打开页面");
  // demo 懒加载及数据滚动加载
  await scrollLoad(page);
  await browser.close();
})();

// 懒加载及数据滚动加载
async function scrollLoad(page) {
  await page.goto("https://www.jd.com/", {
    waitUntil: "networkidle2",
  });
  // setViewport setUserAgent
  // await page.emulate(puppeteer.KnownDevices['iPhone 13 Pro Max']);
  await page.emulate({
    viewport: {
      width: 375,
      height: 667,
      isMobile: true,
    },
    userAgent:
      '"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"',
  });

  // 获取element内容
  const result = await page.$eval("#hotwords", (el) => {
    return el.innerText;
  });
  console.log("page.$eval", result);

  // element 输入
  const inputEle = await page.$("#search input");
  await inputEle.type("macbook pro", {
    delay: 300,
  });

  // element点击
  // const btnEle = await page.$("#search button");
  // await btnEle.click();

  await autoScroll(page);
  //全屏截取
  await page.screenshot({
    path: "jd-all.png",
    fullPage: true,
  });
}

// 工具函数 - 自动滚动
async function autoScroll(page) {
  await page.evaluate(async () => {
    await new Promise((resolve) => {
      let totalHeight = 0;
      const distance = 200;
      const timer = setInterval(() => {
        const scrollHeight = document.body.scrollHeight;
        window.scrollBy(0, distance);
        totalHeight += distance;

        if (totalHeight >= scrollHeight) {
          clearInterval(timer);
          resolve();
        }
      }, 200);
    });
  });
}

参考资料

- Book Lists -