Nodejs监控Apple召回计划&邮件提醒
最近,我的MacBook Pro 2015款13寸电池膨胀了
把笔记本平放在桌面,四个脚中的前两个无法落地,笔记本盖合上之后,屏幕上会印上键盘的纹路,也就是说,笔记本C面D面变形了,已经购买超过3年,售后不给换,同年生产的15寸的MacBook Pro因为同样的问题出了电池召回计划,我想着再坚持一下,看看13寸的会不会也出召回计划
Apple的召回计划全都更新在这里https://support.apple.com/zh-cn/exchange_repair,每天手动去查看一次,不太对得起这台笔记本,干脆写了一个爬虫监控这个页面,有最新消息就邮件通知我,对笔记本来说,也算是“自己的事情自己做”
首先利用axios加载页面,cheerio负责解析,然后找到最新一篇召回计划的标题和链接
import axios from 'axios'; import async from 'async'; import cheerio from 'cheerio'; const URL = 'https://support.apple.com/zh-cn/exchange_repair'; const homePage = await axios.get(URL); const $ = cheerio.load(homePage.data); const firstRepair = $('.as-columns .table-responsive .icon-chevronright'); const firstTitle = firstRepair.eq(0).text(); const firstHref = firstRepair.closest('a').attr('href');
比对标题的开头,是不是以“13 英寸 MacBook Pro”开头的,如果是,用nodemailer以我qq邮箱的身份发邮件给我(发到我的gmail邮箱)
import nodemailer from 'nodemailer';
const reg = /^13 英寸 MacBook Pro/g; // 创建传输器对象 let transporter = nodemailer.createTransport({ service: 'qq', port: 465, secureConnection: true, auth: { // 发件人地址 user: 'xxxx@qq.com', // SMTP授权码 pass: 'xxxx' } }); // 有针对MacBook Pro 13寸的新召回计划,邮件我 if (reg.test(firstTitle)) { let mailOptions = { // 发件人 from: '"【我的定时任务】"xxxx@qq.com', // 收件人 to: 'wangmeijian2016@gmail.com', // 邮件主题 subject: '有针对MacBook Pro 13寸的新召回计划了', // 发送text或者html格式 // text: 'Hello world?', html: `<div> <h3><a style="color: black" href="https://support.apple.com${firstHref}" target="_blank">${firstTitle}</a></h3> <img src="cid:01" /> </div>`, // 附件 attachments: [ { filename: 'Apple.png', path: path.resolve(__dirname, 'Apple.png'), cid: '01', } ] }; transporter.sendMail(mailOptions, (err, info) => { if (err) { return console.log(err); } }); }
其中transporter里的pass不是邮箱密码,而是SMTP授权码,就是授权nodejs用我的qq邮箱发邮件,在qq邮箱【设置】-【账户】里面开启SMTP服务并获取SMTP授权码
你可能注意到我在邮件HTML模板里加入了一张图片Apple.png,并且这张图片的来源就是附件,给附件加个cid就可以在模板里引用了,图片是邮件HTML模板唯一可以引用的外部资源,其他包括字体文件、视频、js文件等都不可引用
另外需要注意的是,我将召回计划的标题颜色设置为黑色,用的是行间样式,是考虑到两个问题
一是兼容性问题,部分邮箱客户端会过滤掉style标签
二是行间样式权重高,web版gmail会给a链接增加一个样式类,设置链接的字体颜色为蓝色,我利用样式权重高的特性,将浏览器给的样式覆盖,从而达到我要的效果
做了个测试,字体颜色前后对比
更多类似兼容性去这里查询
mailOptions还有更多配置,如CC抄送等,更多配置请到这里查看
回到正题,我的需求是每天自动查看一次,此处需要一个定时任务,交给node-schedule
import schedule from 'node-schedule'; // 每天上午9点执行 schedule.scheduleJob('0 9 * * *', () => { // 每天到点干某事 });
综上,完整代码如下
/** * 定时查看Apple召回计划&邮件提醒 */ import schedule from 'node-schedule'; import axios from 'axios'; import async from 'async'; import cheerio from 'cheerio'; import nodemailer from 'nodemailer'; import dayjs from 'dayjs'; import path from 'path'; const formatString = 'YYYY-MM-DD HH:mm'; const timestamp = () => { return dayjs().format(formatString); } const reptile = async () => { const URL = 'https://support.apple.com/zh-cn/exchange_repair'; const homePage = await axios.get(URL); const $ = cheerio.load(homePage.data); const firstRepair = $('.as-columns .table-responsive .icon-chevronright'); const firstTitle = firstRepair.eq(0).text(); const firstHref = firstRepair.closest('a').attr('href'); const reg = /^13 英寸 MacBook Pro/g; // 创建传输器对象 let transporter = nodemailer.createTransport({ service: 'qq', port: 465, secureConnection: true, auth: { user: 'xxx@qq.com', pass: 'xxx' } }); // 有针对MacBook Pro 13寸的新召回计划,邮件我 if (reg.test(firstTitle)) { let mailOptions = { // 发件人 from: '"【我的定时任务】"xxx@qq.com', // 收件人 to: 'wangmeijian2016@gmail.com', // 邮件主题 subject: '有针对MacBook Pro 13寸的新召回计划了', // 发送text或者html格式 // text: 'Hello world?', html: `<div> <h3><a style="color: black" href="https://support.apple.com${firstHref}" target="_blank">${firstTitle}</a></h3> <img src="cid:01" /> </div>`, // 附件 attachments: [ { filename: 'Apple.png', path: path.resolve(__dirname, 'Apple.png'), cid: '01', } ] }; transporter.sendMail(mailOptions, (err, info) => { if (err) { return console.log(err); } else { console.log(`${timestamp()}:邮件已发送~`); } }); }else{ console.log(`${timestamp()}:Apple暂无新召回计划~`); } }; reptile(); console.log(`${timestamp()}:定时任务执行中……`); // 每天上午9点执行 schedule.scheduleJob('0 9 * * *', () => { try { reptile(); } catch (err) { console.log(err); } });
由于nodejs不能直接执行ES6,需要配置一下环境
一、安装babel-node
npm i -g @babel/core @babel/node
二、安装 presets 并配置 .babelrc 文件
npm i @babel/preset-env --save-dev
配置.babelrc
{ "presets": [ "@babel/preset-env" ] }
OK,可以执行了
babel-node xxx.js
……