前端自动化之Puppeteer

Puppeteer是什么

  1. 一个基于Node.js开发的高级库
  2. 提供了对无头(Headless)Chrome或Chromium浏览器的控制能力,使开发人员能够通过编程方式自动化执行各种浏览器操作

Puppeteer应用场景

  1. 网络爬虫:从网页中提取数据、截取网页截图、执行表单提交等。以下是一个简单的示例代码,用于爬取指定网页的标题和描述:
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com');
  
  const title = await page.title();
  const description = await page.$eval('meta[name="description"]', el => el.content);
  
  console.log('Title:', title);
  console.log('Description:', description);
  
  await browser.close();
})();
  1. 自动化测试:模拟用户在浏览器中的交互行为,例如点击按钮、填写表单、检查页面元素等。以下是一个简单的示例代码,用于自动化测试一个登录功能:
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com/login');
  
  await page.type('input[name="username"]', 'myusername');
  await page.type('input[name="password"]', 'mypassword');
  
  await Promise.all([
    page.waitForNavigation(),
    page.click('button[type="submit"]')
  ]);
  
  const successMessage = await page.$eval('.success-message', el => el.textContent);
  console.log('Login successful:', successMessage);
  
  await browser.close();
})();
  1. 网页截图和PDF生成:生成网页的截图或将网页保存为PDF文件,非常适用于生成报告、截取页面快照等需求。以下是一个简单的示例代码,用于将指定网页保存为PDF文件:
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com');
  
  await page.pdf({ path: 'example.pdf', format: 'A4' });
  
  await browser.close();
})();
  1. 网页性能分析:帮助开发人员进行网页性能分析,例如测量页面加载时间、资源加载顺序、DOM节点分布等。这对于优化网页性能和提升用户体验非常有帮助。以下是一个简单的示例代码,用于测量页面的加载时间:
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com');
  const performanceTiming = JSON.parse(
    await page.evaluate(() => JSON.stringify(window.performance.timing))
  );
  
  const pageLoadTime = performanceTiming.loadEventEnd - performanceTiming.navigationStart;
  console.log('页面加载时间:', pageLoadTime, 'ms');
  
  await browser.close();
})();
  1. 表单自动填充:自动填充表单字段,减少重复的手动操作。以下是一个简单的示例代码,用于自动填充表单:
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com/signup');
  
  await page.type('input[name="name"]', 'John Doe');
  await page.type('input[name="email"]', 'johndoe@example.com');
  await page.type('input[name="password"]', 'mypassword');
  
  await page.click('input[name="agree"]');
  
  await page.screenshot({ path: 'filled_form.png' });
  
  await browser.close();
})();
  1. 动态网页内容抓取:模拟用户的交互行为,等待内容加载完成后再进行抓取,从而获取到完整的网页内容。以下是一个简单的示例代码,用于抓取动态生成的内容:
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com/dynamic-content');
  
  await page.waitForSelector('.dynamic-element');
  
  const dynamicContent = await page.$eval('.dynamic-element', el => el.textContent);
  console.log('动态生成内容:', dynamicContent);
  
  await browser.close();
})();
  1. 网页内容监测:定期监测网页内容的变化,定时访问网页,并对比前后的内容差异,以便及时发现变化并采取相应的操作。这对于监测价格变动、新闻更新、产品库存等非常有用。
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // 访问网页并获取初始内容
  await page.goto('https://example.com');
  const initialContent = await page.content();

  // 定时监测网页内容的变化
  setInterval(async () => {
    await page.reload();
    const currentContent = await page.content();

    if (currentContent !== initialContent) {
      console.log('网页内容发生变化!');
      // 在这里执行相应的操作,如发送通知、保存变化等
    }
  }, 60000); // 每分钟检查一次

  // 关闭浏览器
  // await browser.close();
})();
  1. 截取网页元素:截取网页中特定元素的截图,这对于生成网页预览图、保存特定区域的图像等场景非常有帮助。
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto('https://example.com');

  // 选择要截取的元素
  const element = await page.$('h1');

  // 获取元素的位置和大小信息
  const boundingBox = await element.boundingBox();

  // 截取元素对应区域的屏幕截图
  const screenshot = await page.screenshot({
    clip: boundingBox
  });

  // 保存截图到文件
  require('fs').writeFileSync('element-screenshot.png', screenshot);

  await browser.close();
})();
  1. 网页交互录制与回放:记录用户在网页上的交互操作,并生成可重放的脚本。测试人员可以记录用户的操作步骤,然后回放这些操作,以验证网页的行为和功能。
js 复制代码
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // 启用页面交互录制
  await page.evaluateOnNewDocument(() => {
    const events = [];

    // 监听页面交互事件并记录到数组中
    document.addEventListener('click', e => {
      events.push({
        type: 'click',
        target: e.target.toString()
      });
    });

    // 将记录的事件数组保存到localStorage中
    localStorage.setItem('recordedEvents', JSON.stringify(events));
  });

  // 导航到目标页面
  await page.goto('https://example.com');

  // 停止页面交互录制
  await page.evaluate(() => {
    localStorage.removeItem('recordedEvents');
  });

  // 从localStorage中获取录制的事件
  const recordedEvents = await page.evaluate(() => {
    const events = localStorage.getItem('recordedEvents');
    return JSON.parse(events);
  });

  // 回放录制的事件
  for (const event of recordedEvents) {
    await page.evaluate(event => {
      const target = eval(event.target);
      const clickEvent = new MouseEvent('click', {
        view: window,
        bubbles: true,
        cancelable: true
      });
      target.dispatchEvent(clickEvent);
    }, event);
  }

  await browser.close();
})();

问题解决

在使用过程中可能会有一些限制和常见的问题,以下是其中一些以及如何解决的建议:

  1. 内存消耗:使用Chrome浏览器实例来执行操作会消耗一定的内存。对于大规模的自动化任务或长时间的运行,可能会导致内存占用过高。解决方法包括使用--max-old-space-size标志限制Chrome实例的内存使用,或在长时间任务中定期重启浏览器实例。
javascript 复制代码
const browser = await puppeteer.launch({ args: ['--max-old-space-size=4096'] });
  1. 页面加载超时:当页面加载时间较长或网络不稳定时,Puppeteer默认的页面加载超时时间可能不足以完成加载。可以通过设置page.setDefaultNavigationTimeout()page.goto()方法的timeout参数来增加超时时间。
javascript 复制代码
// 设置默认的超时时间
page.setDefaultNavigationTimeout(60000);

// 在特定的页面跳转中设置超时时间
await page.goto('https://example.com', { timeout: 60000 });
  1. 页面元素不可见或不可交互:在执行某些操作时,可能会遇到元素不可见或不可交互的情况。这可能是因为页面尚未完全加载或元素被其他元素遮挡。可以使用page.waitForSelector()方法来等待元素的出现和可见性,或者使用page.waitForNavigation()方法等待页面完全加载。
javascript 复制代码
// 等待元素可见
await page.waitForSelector('.target-element', { visible: true });

// 等待页面加载完成
await page.waitForNavigation({ waitUntil: 'networkidle0' });
  1. 反爬虫措施:一些网站会采取反爬虫措施,如检测自动化工具或滥用行为。为了避免被检测到,可以模拟人类的行为模式,例如设置随机的延迟时间、模拟鼠标移动等,以增加自动化的真实性。
javascript 复制代码
// 设置随机延迟时间
const delay = Math.random() * 1000 + 1000; // 1000ms 到 2000ms 之间的延迟
await page.waitForTimeout(delay);
  1. CAPTCHA(验证码):某些网站可能会使用CAPTCHA来验证用户身份。对于自动化工具,处理CAPTCHA可能会很困难。一种解决方法是使用第三方的验证码识别服务,例如打码平台,将验证码图片上传并获取识别结果,然后将结果输入到相应的输入框中。
js 复制代码
const puppeteer = require('puppeteer');
const axios = require('axios');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto('https://example.com');

  // 检测是否需要处理验证码
  const captchaElement = await page.$('#captcha-img');
  if (captchaElement) {
    // 获取验证码图片的 URL
    const captchaImageUrl = await page.evaluate(element => element.src, captchaElement);

    // 下载验证码图片
    const response = await axios.get(captchaImageUrl, { responseType: 'arraybuffer' });
    const captchaImageBuffer = Buffer.from(response.data, 'binary');

    // 将验证码图片上传到打码平台并获取识别结果
    const captchaResult = await uploadAndRecognizeCaptcha(captchaImageBuffer);

    // 输入识别结果到验证码输入框
    const captchaInput = await page.$('#captcha-input');
    await captchaInput.type(captchaResult);
  }

  // 继续执行其他操作

  // 关闭浏览器
  // await browser.close();
})();

async function uploadAndRecognizeCaptcha(imageBuffer) {
  // 将验证码图片上传到打码平台的 API
  const response = await axios.post('https://api.example.com/captcha', {
    image: imageBuffer.toString('base64')
  });

  // 解析打码平台的响应,获取识别结果
  const captchaResult = response.data.result;

  return captchaResult;
}

写在最后

本博文通篇使用了尽可能简短直白的描述与代码示例从概念、场景、开发中可能遇到的问题及解决方案三个方面做了一个通识分享,希望你们有所收获。保持学习,共勉~

相关推荐
前端啊龙3 分钟前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠7 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds27 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm
理想不理想v3 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试