User-Profile-Image
hankin
  • 5
  • 首页
  • 留言板
  • 贺卡
  • 密码本
  • 五码合一
  • 分类
    • 有感而发
    • 效率
    • 技术
    • 心境
    • 动漫
  • 页面
    • 留言板
  • 友链
    • 仙小宅
Help?

Please contact us on our email for need any support

Support
  • 首页
  • 留言板
  • 贺卡
  • 密码本
  • 五码合一
    首页   ›   技术   ›   正文
技术

使用node+puppeteer+express搭建截图服务_仙小宅_zip,程序

2021-01-04 23:02:27
21  0 0

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

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

写在之前

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

准备

由于整个系统是基于node+express的web服务,puppeteer只是node的一个plugin,所以需要做的准备大致有下

  • 一台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】

  • npm install pm2 -g
  • 其它操作请见https://pm2.keymetrics.io

安装字体

【这个其实很重要,我也绕了弯,原本以为改改字体编码就可以了,后来发现不是】

,https://b.xz6.co,

  • 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 express

    [注意:如果安装失败 请检查是否更改为taobao源]

启动及管理

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

使用

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

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

最后

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

SparkStreaming推测机制:面试被问遇到什么问题,说这个显水平!

#

评论 (0)

点击这里取消回复。

欢迎您 游客  

不羡江中仙
一叶一菩提,一花一世界
6446文章 8评论 14点赞 186367浏览

忆念

林深时见鹿,海蓝时见鲸,梦醒时见你。 可实际, 林深时雾起,海蓝时浪涌,梦醒时夜续。 未见鹿,未见鲸,亦未见你。 但, 鹿踏雾而来,鲸随浪而起,你没回头 又怎知我没来。

标签云
api asp c core docker github java js jvm kubernetes linux mysql net python redis seo spring springboot sql vite vue web 代码 优化 内存 函数 学习 对象 并发 按钮 接口 搜索引擎 数据 数据库 服务 服务器 模式 源码 算法 线程 组件 绑定 编程 网站 项目
近期文章
  • 决定 2021年1月23日
  • 使用node+puppeteer+express搭建截图服务_仙小宅_zip,程序 2021年1月4日
  • 【Go】四舍五入在go语言中为何如此困难_仙小宅_仙小宅,感情 2021年1月4日
  • 在 Emit 代码中如何await一个异步方法_仙小宅_仙小宅,python 2021年1月4日
  • 对各向异性高光的理解_仙小宅_版本升级,心理 2021年1月4日
文章归档
  • 2021年一月 (49)
  • 2020年十二月 (1)
  • 2020年十一月 (92)
  • 2020年十月 (140)
  • 2020年八月 (266)
  • 2020年七月 (987)
  • 2020年六月 (958)
  • 2020年五月 (716)
  • 2020年四月 (1137)
  • 2020年三月 (1133)
  • 2020年二月 (946)
  • 2020年一月 (2)
  • 2019年十二月 (19)
博客统计
  • 日志总数:6446 篇
  • 评论数目:8 条
  • 建站日期:2019-11-11
  • 运行天数:439 天
  • 标签总数:5341 个
  • 最后更新:2021-1-23
Copyright © 2021 网站备案号: 冀ICP备12010494号
主页
页面
  • 留言板
博主
不羡江中仙
不羡江中仙 管理员
不羡江中仙
6446 文章 8 评论 186367 浏览
测试
测试