现代化爬虫神器-Puppeteer

前言

最近想玩一下node的爬虫,发现crawler的爬取功能十分受限,特别是在现在满大街都是vue,angular,react等前端框架写的网页的情况下,crawler就不能爬取js动态生成的内容了,这十分鸡肋。于是,研究了很久,找到了利用chromium作为沙盒环境进行爬虫的puppeteer爬虫框架,他的功能原本是模拟进行自动化测试的,可以捕获页面的截屏,获取页面的内容等,用来做动态内容的抓取,功能十分强大。示例及demo我放在github上了,可自取。

puppeteer介绍

是什么

一句话总结:调用chrome开放的api并且操纵chromium去实现复杂的抓取业务逻辑的js接口封装库。

puppeteer默认以headless模式控制 Chrome浏览器来运行(headless chrome),它的最大优点就是可以爬取SPA页面或者动态渲染的页面内容,还能模拟用户行为。

几个概念:
headless:无头模式,无界面的
headless browser:简易的无GUI界面渲染的browser程序,并暴露一些api给程序调用
headless chrome:无界面的环境下运行chrome浏览器,headless browser的一种

能做什么

  • 生成页面 PDF。
  • 抓取 SPA(单页应用)并生成预渲染内容(即“SSR”(服务器端渲染))。
  • 自动提交表单,进行 UI 测试,键盘输入等。
  • 创建一个时时更新的自动化测试环境。 使用最新的 JavaScript 和浏览器功能直接在最新版本的Chrome中执行测试。
  • 捕获网站的 timeline trace,用来帮助分析性能问题。
  • 测试浏览器扩展。

运行原理

使用DevTools协议与浏览器进行通信

api结构

现代化爬虫神器-Puppeteer_第1张图片
puppeteer api 结构.png

Puppeteer 使用 DevTools 协议 与浏览器进行通信。
Browser 实例可以拥有浏览器上下文。
BrowserContext 实例定义了一个浏览会话并可拥有多个页面。
Page 至少有一个框架:主框架。 可能还有其他框架由 iframe 或 框架标签 创建。
frame 至少有一个执行上下文 - 默认是JavaScript 被执行的上下文 。
Worker 具有单一执行上下文,并且便于与 WebWorkers 进行交互

puppeteer示例

1. 截图,生成pdf

// 截屏,生成pdf
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch({ headless: false, slowMo: 0 }); // 启动一个浏览器,headless默认为true
  const page = await browser.newPage(); // 打开一个新页面
  await page.goto('http://example.com'); // 打开某个网址
  await page.screenshot({ path: 'test/image/example.png' }); // 执行截图
  // await page.pdf({ path: 'test/pdf/example.pdf', format: 'A4' }); // 创建一个pdf,headless需为true
  await browser.close(); // 关闭浏览器
})()

运行结果:
现代化爬虫神器-Puppeteer_第2张图片
截图,生成pdf.png

2. 自动提交表单

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch({ headless: false })
  const page = await browser.newPage()
  await page.goto('http://www.baidu.com')
  await page.type('#kw', 'puppeteer') // 键盘输入关键字
  await page.waitFor(1000)
  await page.click('#su') // 模拟用户点击搜索提交表单
  await page.waitFor(2000)
  await page.screenshot({ path: 'test/image/search.png', fullPage: true }) // 截全屏
  await browser.close()
})()

运行结果:


现代化爬虫神器-Puppeteer_第3张图片
自动提交表单.png

3. UI自动化测试

// ui测试
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6'];
(async () => {
  const browser = await puppeteer.launch({ headless: false, slowMo: 0 });
  const page = await browser.newPage();
  await page.emulate(iPhone); // 让页面模拟成iphone6
  await page.goto('https://m.v.qq.com/play.html?cid=rjvr8psrvic4567&vid=')
  await page.screenshot({ path: 'test/image/1.png' })
  // 点击登录
  await page.click('.btn_user_text')
  await page.waitFor(1000)
  await page.screenshot({ path: 'test/image/2.png' })
  // 点击qq登录
  await page.waitFor(1000)
  await page.click('.btn_qq')
  await page.waitFor(1000)
  await page.screenshot({ path: 'test/image/3.png' })
  // 输入账号密码
  await page.type('#u', '******') // 账号
  await page.type('#p', '******') // 密码
  await page.screenshot({ path: 'test/image/4.png' })
  await page.waitFor(1000)
  // 点击登录
  await page.click('#go')
  await page.waitFor(1000)
  await page.screenshot({ path: 'test/image/5.png' })
  await page.waitFor(1000)

  await browser.close();
})()

运行结果:


现代化爬虫神器-Puppeteer_第4张图片
ui自动化测试.png

4. 爬取动态渲染网页内容

// 爬虫
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch({ headless: false, slowMo: 0 });
  const page = await browser.newPage();
  await page.setViewport({  // 设置viewport大小
    width: 375,
    height: 600,
    isMobile: true,
    hasTouch: true
  })
  await page.goto('https://www.bilibili.com/');

  let list = await page.evaluate(() => {  // 爬取内容
    const title = document.querySelectorAll('.ri-title')
    const elements = Array.from(title);
    let titles = elements.map(element => {
      return element.innerHTML
    })
    return titles
  });
  console.log(list)

  await page.waitFor(1000) // 等待时长
  await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

  await browser.close();
})()

运行结果:


现代化爬虫神器-Puppeteer_第5张图片
爬取动态渲染页面内容.png

5. 分析页面性能

// 性能分析
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6'];

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.emulate(iPhone); // 让页面模拟成iphone6
  await page.tracing.start({ path: 'test/doc/trace.json' }); // 生成页面性能追踪的文件
  await page.goto('https://www.bilibili.com/');
  await page.tracing.stop();
  browser.close();
})();

获取到的json文件导入chrome的开发者工具的performance下,可以得到页面性能分析图表。

6. 创建不同版本的chrome进行测试

const browserFetcher = puppeteer.createBrowserFetcher();
const revisionInfo = await browserFetcher.download('533271');
const browser = await puppeteer.launch({executablePath: revisionInfo.executablePath})

总结

在项目开发中,如果直接由前端就能完成爬虫任务的话,使用puppeteer不失为一个好的选择,这样既可以免去前后端的对接时间,又能更高效开发;对于测试人员而言,使用puppeteer进行自动化测试,也能节省很多重复性工作。

参考文章

  • puppeteer文档
  • 实例:使用puppeteer headless方式抓取JS网页
  • Node 使用 puppeteer 抓取动态页面

你可能感兴趣的:(现代化爬虫神器-Puppeteer)