最近,我的13寸 MacBook Pro 2015款電池膨脹了
把筆記本平放在桌面,四個腳中的前兩個無法落地,筆記本蓋合上之後,螢幕上會印上鍵盤的紋路,也就是說,筆記本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';
import path from 'path';
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
……