一开始咱们的需求是打开报表的某个页面而后把图截出来,而后调用企业微信发送给业务群
这中间我尝试了多种技术,好比html2image
,pdf2image
、selenium
这些,这其中截图
比体验较好的也就selenium
了,不过咱们有些页面加载的时间较长,selenium彷佛对html互操做性
也不是很完美(经过Thread.sleep并不能完美的兼容绝大多数报表),另外还有一个比较要命的
是Chromium渲染出来的页面彷佛也有不一样程度的问题(就是很差看),固然后面一个偶然的机会在
某不知名网站看到有网友用puppeteer
来实现截图,遂~,一通骚操做就搭了一套出来(虽然最终方案并非这个
,固然这是后话哈~),这里就拿出来讲说哈~node
因为整个系统是基于node+express的web服务,puppeteer只是node的一个plugin,因此须要作的准备大体有下linux
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前必须安装npm,若是只是非正式环境能够不用安装pm2】web
npm install pm2 -g
【这个其实很重要,我也绕了弯,本来觉得改改字体编码就能够了,后来发现不是】express
// 引入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); });
{ "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
npm
[注意:若是安装失败 请检查是否更改成taobao源]json
node index.js
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