现代化爬虫神器-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 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(); // 关闭浏览器
})()

运行结果:
截图,生成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()
})()

运行结果:


自动提交表单.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();
})()

运行结果:


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

运行结果:


爬取动态渲染页面内容.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进行自动化测试,也能节省很多重复性工作。

参考文章

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容