Node后台定时浏览器截图并发送到钉钉群
背景:我们部门有个数据看板页面需要定时截图到群里和组内成员同步信息,之前都是手动截图,这种方式有时候会忘,而且时间不可控,所以我就考虑是否可以通过node后台实现自动截图并发送到钉钉群。
实现功能主要为以下几个部分
1.搭建node后台
具体请参考我之前的文章一个前端开发工程师从零开始开发Node到发布的过程
2.通过puppeteer实现网页加载、截图功能
第一步就是安装puttteer这个无头浏览器
import puppeteer from 'puppeteer'
第二步打开网页
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'] //在linux中会报错sandbox之类的错误
})
try {
let token = await login()
if (!token) {
return
}
let url = `${baseUrl}/dashboard/index.html?token=${token}`
const page = await browser.newPage()
page.setViewport({
width: 1920,
height: 1080
})
await page.goto(url, {
timeout: 50000
})
await page.waitFor(4000)
// await waitTime(4000)
await page.screenshot({
type: 'png',
path: imgPath, //本地存储图片的路径,自定义设置即可
fullPage: true
})
} catch (error) {
console.log(error)
} finally {
await browser.close()
}
注意事项:
1.在centos中会报错sandbox之类的参数,需要在浏览器初始化的时候添加参数
2.setViewport设置页面的宽高,按需求设置
3.page.goto参数timeout是打开网页的超时时间,我这边设置的50s,按需设置
4.page.waitFor(4000) page自带的等待时间,比如说页面是前后端分离的,我们需要等ajax请求数据完成之后再截图,这个时候就需要这个参数了。当然,还有另外一种方式,就是在goto方法中使用waitUntil参数,具体参数请参考
api文档, 第三种方式就是自己通过promise实现一个waitTime方法,等待相应时间。
5.page.screenshot就是截图方法。
6.最后,最重要的一点就是一定要把截图放到try catch中,最后在finally中关闭浏览器,否则如果报错浏览器未关闭,服务器的内存一定很感人。
3.上传图片到金山云
这个相对来说比较简单,安装ks3后通过)使用ks3,AK,SK请问后台的同学要一下。
let KS3 = require('ks3'
// AK = AccessKey; SK = SecretKey
let ks3 = new KS3(ACCESS_KEY_ID, ACCESS_KEY_SECRET)
ks3.object.put() //使用该方法上传本地图片
上传完图片之后就得到一个线上的金山云地址,注意,上传的时候需要将图片的权限设置为ACL: 'public-read'
4.创建定时任务
定时任务使用的是「node-schedule」,我这边定义的是每天十点钟发一次,具体规则请参考 github文档
import schedule from 'node-schedule'
// 定义规则
let rule = new schedule.RecurrenceRule()
rule.hour = 10 // 时
rule.minute = 0 // 分
rule.second = 0 // 秒
schedule.scheduleJob(rule, () => {
action() // 执行一系列方法,包括截图,上传,发送钉钉
})
5.发送信息到钉钉群
钉钉有相应的api文档,不过我用的是一位老哥封装好的库dingtalk-robot-sender,非常好用
1.在钉钉群里添加自定义机器人,添加完成之后会生成一个webhook,请复制这个地址,我们在发送钉钉消息的时候需要用到。
2.添加机器人的时候需要添加自定义关键字,发送消息的时候需要包含这些关键字中的一个或多个才能发送成功。
3.可以直接调用封装好的markdown、text、link、text等方法直接发送消息。
这个库使用起来非常简单,推荐使用。
import ChatBot from 'dingtalk-robot-sender'
// 直接使用 webhook
const robot = new ChatBot({
webhook: `https://oapi.dingtalk.com/robot/send?access_token=${webShotToken}`
})
const title = '关键字'
const text =
`#### ${format('yyyy-MM-dd hh:mm')} 板数据(${xxx})\n` +
`##### xxxxx:${data}\n\n` +
`##### \n\n`
const at = {
isAtAll: false,
atMobiles: ['']
}
robot.markdown(title, text, at)
6.发布的使用遇到的坑
1.我使用的是pm2管理node发布,会启动4个子进程,代码中,我是在初始化的时候启动定时任务,这个时候就会出现一个问题,定时任务被启动了4次,这个肯定无法接受。
解决办法就是通过进程id来判断是哪个进程,然后当进程id为0时才初始化定时任务。
可以通过process.env中的INSTANCE_ID这个字段来判断。这个字段其实是我在pm2的配置文件中配置的,配置这个的原因是因为pm2和config这个库发生的冲突,所以需要自定义一个变量名称。如果没有配置的话,可以使用'NODE_APP_INSTANCE'这个变量获取进程id。
"instance_var": "INSTANCE_ID",
process.env.INSTANCE_ID === '1
2.正所谓一波未平一波又起,刚解决pm2的v问题,新的问题又来了,我们线上项目同时部署在两台服务器上,这样就出现同时是启动两个定时任务的情况,好尴尬。兵来将挡水来土掩。这次的解决办法就是判断服务器的ip,两台服务器都是有自己的ip地址的,只要锁定一台服务器ip,只在这台服务器上启动定时任务就好了,当然这不是最优解,最好的办法其实是单独一台服务器专用于定时任务。
获取IP地址的方法如下
export function getIPAdress() {
let interfaces = require('os').networkInterfaces()
for (let devName in interfaces) {
let iface = interfaces[devName]
for (let i = 0; i < iface.length; i++) {
let alias = iface[i]
if (
alias.family === 'IPv4' &&
alias.address !== '127.0.0.1' &&
!alias.internal
) {
return alias.address
}
}
}
}
最后的最后,服务上线之后发现页面中文字体好多显示方块。。。。崩溃。。。
那就继续找解决办法,我们的服务器是centos 7.5,网上很多解决办法就是把windows的字体文件拷贝到centos中,我觉得这个方法有点麻烦。。。所以我就继续找。终于找到一个方法。执行这个方法就好了。
yum groupinstall Fonts
一切搞定~ 每天十点见。 有什么问题请留言指正哈~