现代化爬虫神器-Puppeteer

作者: xurna | 来源:发表于2019-05-28 16:20 被阅读1次

    前言

    最近想玩一下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进行自动化测试,也能节省很多重复性工作。

    参考文章

    相关文章

      网友评论

        本文标题:现代化爬虫神器-Puppeteer

        本文链接:https://www.haomeiwen.com/subject/uenqvqtx.html