从零开始学习 puppeteer

简介: 从零开始学习 puppeteer

image.png


关键词:自动化测试 爬虫 chrome

文档 EN: pptr.dev/ CN: puppeteer.bootcss.com/

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

通过这篇文章你会了解到:

  1. puppeteer 环境安装
  2. puppeteer 基本使用
  3. 滚动截屏
  4. 模拟点击获取数据
  5. 定时任务获取数据


安装


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


拓展 - 点击按钮获取数据


B 站有个换一换的按钮,我们就拿这个功能举例:

首先就是大致一样的页面逻辑:

const puppeteer = require("puppeteer");
const fs = require("fs-extra");
async function demo() {
  const browser = await puppeteer.launch({
    headless: "new",
  });
  console.log("打开浏览器");
  const page = await browser.newPage();
  console.log("打开页面");
  // 页面操作
  await dealLogically(page, browser);
}
demo();


然后再进行业务定制:

async function dealLogically(page, browser) {
  await page.setViewport({ width: 1920, height: 1080 });
  // 打开目标页面
  await page.goto("https://www.bilibili.com/");
  // 返回的data
  let data = [];
  // 获取按钮
  const change = await page.$(".feed-roll-btn > .roll-btn");
  // 点击10次
  for (let i = 0; i < 10; i++) {
    // 模拟点击事件
    change && (await change.click());
    // 每次点击之后,留出1s给页面资源加载
    await page.waitForTimeout(1000);
    data = await page.evaluate((data) => {
      const titles = document.querySelectorAll(
        ".container .feed-card .bili-video-card__info--tit"
      );
      const imgs = document.querySelectorAll(
        ".container .feed-card .bili-video-card__cover img"
      );
      const info = document.querySelectorAll(
        ".container .feed-card .bili-video-card__info--owner"
      );
      titles.forEach((v, i) => {
        const [owner, date] = info[i].textContent.split("· ");
        data.push({
          img: imgs[i].getAttribute("src"),
          title: v.textContent,
          info: {
            owner,
            date,
          },
        });
      });
      return data;
    }, data);
  }
  // 关闭浏览器实例
  await browser.close();
  // 将数据作为 JSON 写入json文件
  const jsonData = JSON.stringify(data, null, 2);
  //写入文件路径
  const filePath = `./${+new Date()}.json`;
  fs.writeFile(filePath, jsonData, (err) => {
    if (err) {
      console.error(err);
      return;
    }
    console.log("Data written to file");
  });
}


拓展 - 定时任务


引入 node-schedule 这个包。

npx nodemon index 运行项目。


const schedule = require("node-schedule");
// 引入上面b站的案例代码
const demo = require("../demo");
function main() {
  schedule.scheduleJob(`自动爬虫任务`, `30 20 * * * *`, ()=>demo());
}
main();

拓展 - 模拟登录


通过 cookie 模拟已登录环境,推荐使用 "Export cookie JSON file for Puppeteer" 这个 chrome 插件直接获取cookie 信息。

举例:模拟登陆掘金


const puppeteer = require("puppeteer");
// 通过 插件 获取的 JSON化 cookie
const cookieObjects = require("./cookies.json");
(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.setViewport({ width: 1920, height: 1080 });
  cookieObjects.forEach((cookie) => {
    page.setCookie(cookie);
  });
  await page.goto("https://juejin.cn");
})()


参考资料



目录
相关文章
|
7月前
|
移动开发 前端开发 JavaScript
从零开始学习前端开发:入门指南
本文将介绍从零开始学习前端开发的入门指南。通过学习HTML、CSS和JavaScript等基础知识,读者将了解前端开发的基本概念和工具,并学会如何构建简单的网页应用程序。无论您是初学者还是有一定经验的开发人员,本文都将帮助您打下坚实的前端开发基础。
|
6月前
|
开发框架 JavaScript 前端开发
技术经验解读:从零开始学习jQuery(十)jQueryUI常用功能实战
技术经验解读:从零开始学习jQuery(十)jQueryUI常用功能实战
46 0
|
7月前
|
前端开发 JavaScript
从零开始学习前端开发
前端开发是一门非常受欢迎的技术,它可以让我们在网页上展示出美观、交互式的内容。但是对于初学者来说,前端开发可能是一门比较难入手的技术。本文将会从基础概念开始介绍前端开发,并深入了解HTML、CSS和JavaScript的使用及其应用。
|
资源调度 前端开发 JavaScript
从零开始学习React-开发环境的搭建(一)
从零开始学习React-开发环境的搭建(一)
79 0
|
存储 程序员 C语言
从零开始学C
从零开始学C
59 0
|
编解码 JavaScript 前端开发
🏆从零开始学习JS进阶1️⃣🏆
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数或方法,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
142 0
🏆从零开始学习JS进阶1️⃣🏆
|
存储 JavaScript 索引
🚀从零开始学习JS基础8️⃣🚀
数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式。
128 0
🚀从零开始学习JS基础8️⃣🚀
|
存储 JavaScript 前端开发
🚀从零开始学习JS基础9️⃣🚀
 在 JS 里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用。虽然 for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时我们就可以使用 JS 中的函数。     简答来说,函数就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。
122 0
🚀从零开始学习JS基础9️⃣🚀
|
JavaScript
🚀从零开始学习JS基础7️⃣🚀
断点调试是指自己在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。断点调试可以帮助观察程序的运行过程。
122 0
🚀从零开始学习JS基础7️⃣🚀
|
JavaScript
🚀从零开始学习JS基础6️⃣🚀
在一个程序执行的过程中,各条代码的执行顺序对程序的结果是有直接影响的。很多时候我们要通过控制代码的执行顺序来实现我们要完成的功能。简单来说,流程控制就是来控制代码按照一定结构顺序来执行。
102 0
🚀从零开始学习JS基础6️⃣🚀