使用node+puppeteer+express搭建截图服务

使用node+puppeteer+express搭建截图服务

转载请注明出处https://www.cnblogs.com/funnyzpc/p/14222807.htmlhtml

写在以前

一开始咱们的需求是打开报表的某个页面而后把图截出来,而后调用企业微信发送给业务群
这中间我尝试了多种技术,好比html2imagepdf2imageselenium这些,这其中截图
比体验较好的也就selenium了,不过咱们有些页面加载的时间较长,selenium彷佛对html互操做性
也不是很完美(经过Thread.sleep并不能完美的兼容绝大多数报表),另外还有一个比较要命的
是Chromium渲染出来的页面彷佛也有不一样程度的问题(就是很差看),固然后面一个偶然的机会在
某不知名网站看到有网友用puppeteer来实现截图,遂~,一通骚操做就搭了一套出来(虽然最终方案并非这个
,固然这是后话哈~),这里就拿出来讲说哈~node

准备

因为整个系统是基于node+express的web服务,puppeteer只是node的一个plugin,因此须要作的准备大体有下linux

  • 一台linux服务器,这里实用centos
  • node安装包(用于搭建node环境)
  • 字体文件

安装node环境

  • wget https://nodejs.org/dist/v14.15.3/node-v14.15.3-linux-x64.tar.xz
  • tar --strip-components 1 -xvJf node-v* -C /usr/local
  • npm config set registry https://registry.npm.taobao.org

安装pm2(用于守护node服务)

【注意:安装pm2前必须安装npm,若是只是非正式环境能够不用安装pm2】web

安装字体

【这个其实很重要,我也绕了弯,本来觉得改改字体编码就能够了,后来发现不是】express

  • step1: 将window字体复制到linux下
    • windows: C:\Windows\Fonts
    • Linux: /usr/share/fonts/
  • step2: 创建字体索引信息并更新字体缓存
    • cd /usr/share/fonts/
    • mkfontscale
    • mkfontdir
    • fc-cache

准备代码

  • index.js
// 引入express module
// 引入puppeteer module
const express = require('express'),
    app = express(),
    puppeteer = require('puppeteer');

// 函数::页面加载监控
const waitTillHTMLRendered = async (page, timeout = 30000) => {
  const checkDurationMsecs = 1000;
  const maxChecks = timeout / checkDurationMsecs;
  let lastHTMLSize = 0;
  let checkCounts = 1;
  let countStableSizeIterations = 0;
  const minStableSizeIterations = 3;

  while(checkCounts++ <= maxChecks){
    let html = await page.content();
    let currentHTMLSize = html.length;
    let bodyHTMLSize = await page.evaluate(() => document.body.innerHTML.length);
    console.log('last: ', lastHTMLSize, ' <> curr: ', currentHTMLSize, " body html size: ", bodyHTMLSize);
    if(lastHTMLSize != 0 && currentHTMLSize == lastHTMLSize)
      countStableSizeIterations++;
    else
      countStableSizeIterations = 0; //reset the counter

    if(countStableSizeIterations >= minStableSizeIterations) {
      console.log("Page rendered fully..");
      break;
    }
    lastHTMLSize = currentHTMLSize;
    await page.waitFor(checkDurationMsecs);
  }
};

//建立一个 `/screenshot` 的route
app.get("/screenshot", async (request, response) => {
  try {
        const browser = await puppeteer.launch({ args: ['--no-sandbox'] });
        const page = await browser.newPage();
        await page.setViewport({
                            width:!request.query.width?1600:Number(request.query.width),
                            height:!request.query.height?900:Number(request.query.height)
                                                        });
        // 这里执行登陆操做(非公共页面须要登陆)
        if(request.query.login && request.query.login=="true"){
                // wait until page load
                await page.goto('认证(登陆)地址', { waitUntil: 'networkidle0' });
                await page.type('#username', '登陆用户名');
                await page.type('#password', '登陆密码');
                // click and wait for navigation
                await Promise.all([
                        page.click('#loginBtn'),
                        page.waitForNavigation({ waitUntil: 'networkidle0' }),
                ]);
        }
        await page.goto(request.query.url,{'timeout': 12000, 'waitUntil':'load'});
        await waitTillHTMLRendered(page);
    const image = await page.screenshot({fullPage : true,margin: {top: '100px'}});
    await browser.close();
    response.set('Content-Type', 'image/png');
    response.send(image);
  } catch (error) {
    console.log(error);
  }
});
// listener 监听 3000端口
var listener = app.listen(3000, function () {
  console.log('Your appliction is listening on port ' + listener.address().port);
});
  • package.json
{
  "name": "funnyzpc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

依赖安装

  • npm i --save puppeteer expressnpm

    [注意:若是安装失败 请检查是否更改成taobao源]json

启动及管理

  • 直接使用node启动服务
    • node index.js
  • 使用pm2启动(若是安装了pm2)
    • 启动:pm2 start index.js
    • 进程:pm2 list
    • 删除:pm2 delete 应用ID

使用

因为以上代码已经对截图的加载作过处理的,因此无需在使用线程睡眠
同时代码也对宽度(width)和高度(height)作了处理,因此具体访问地址以下windows

http://127.0.0.1:3000/screenshot/?login=[是否登陆true or false]&width=[页面宽度]&height=[页面高度]&url=[截图地址]centos

最后

虽然咱们咱们使用puppeteer能应对绝大多数报表,后来发现puppeteer对多组件图表存在渲染问题,因此就要求
提供商提供导出图片功能(用户页面导出非api),因此最终一套就是 http模拟登陆+调用截图接口+图片生成监控+推送图片
好了,关于截图就分享到这里了,各位元旦节快乐哈~《@.@》api

相关文章
相关标签/搜索