教你撩妹,每天给小姐姐发一封暖心邮件
寫在前面
自從用郵箱注冊(cè)了很多賬號(hào)后,便會(huì)收到諸如以下類似的郵件,剛開始還以為是一張圖片,后來仔細(xì)一看不是圖片呀,好像還是 HTML 呀,于是好奇寶寶我 Google 一下,查閱多篇資料后總結(jié)出怎么用前端知識(shí)和 Node 做一個(gè)這樣的“郵件網(wǎng)頁”。
確認(rèn)主題
知道怎么實(shí)現(xiàn)功能后,思考著我該寫什么主題呢,用一個(gè) HTML 模板隨便給小伙伴們發(fā)個(gè)郵件炫個(gè)技?不行,作為一個(gè)很 cool 的程序員怎么能這么 low 呢,最近天氣變化幅度大,溫度捉摸不定,女朋友總是抱怨穿少了又冷穿多了又熱,嗨呀,要不我就寫個(gè)每天定時(shí)給寶寶發(fā)送天氣預(yù)報(bào)的郵件,另外想起寶寶喜歡看 ONE·一個(gè)這個(gè)APP上的每日更新,要不發(fā)天氣預(yù)報(bào)的同時(shí),再附贈(zèng)一個(gè)“ONE 的每日訂閱”?機(jī)智又浪漫,開始搬磚~
劇透
本來是想最后放效果圖的,怕你們看到一半就沒興趣了,就在前面劇透一下我最后做出來的效果圖吧~
待解決的問題
1. 如何獲取天氣預(yù)報(bào)和 ONE 上的 data?
答:獲取data有兩種方法,第一種方法是獲取天氣預(yù)報(bào)和 ONE 的 API,第二種是用 node 爬蟲獲取天氣預(yù)報(bào)和 ONE 網(wǎng)頁的信息。后來找了下,發(fā)現(xiàn) ONE 并沒有 API 接口,為了讓兩者統(tǒng)一,于是決定使用 node 上的一個(gè)插件叫 cheerio,配合 superagent能夠很方便地爬取網(wǎng)頁上的信息。
2. 如何做出 HTML 的這種郵件?
答:之前學(xué)過一段時(shí)間的 express 這個(gè)框架,接觸到模版引擎這個(gè)概念,傳入data 便可獲得 html 文件,再結(jié)合 node 的 fs 模塊,獲取到這個(gè) html 文件,便可以結(jié)合 node 的郵件插件發(fā)送 HTML 郵件啦!
3. 如何用 node 發(fā)送郵件?
感謝無私的開源開發(fā)者,開發(fā)了一款發(fā)送郵件的 Node 插件 nodemailer,兼容主流的 Email 廠商,只需要配置好郵箱賬號(hào)和 smtp 授權(quán)碼,便可以用你的郵箱賬號(hào)在 node 腳本上發(fā)文件,很 cool 有沒有~
4. 如何做到每日定時(shí)發(fā)送?
其實(shí)可以通過各種 hack 的方式寫這么一個(gè)定時(shí)任務(wù),但是既然 node 社區(qū)有這個(gè)定時(shí)的輪子,那我們直接用就好了, node-schedule是一個(gè)有著各種配置的定時(shí)任務(wù)發(fā)生器,可以定時(shí)每個(gè)月、每個(gè)禮拜、每天具體什么時(shí)候執(zhí)行什么任務(wù),這正符合每天早晨定時(shí)給寶寶發(fā)送郵件的需求。
一切準(zhǔn)備就緒,開始做一次浪漫的程序員
編寫代碼
網(wǎng)頁爬蟲
這里我們使用到 superagent和 cheerio組合來實(shí)現(xiàn)爬蟲:
分析網(wǎng)頁 DOM 結(jié)構(gòu),如下圖所示:
用 superagent 來獲取指定網(wǎng)頁的所有 DOM:
用 cheerio 來篩選 superagent 獲取到的 DOM,取出需要的 DOM
以下就是爬取 ONE 的代碼,天氣預(yù)報(bào)網(wǎng)頁也是一個(gè)道理:
const superagent = require('superagent'); //發(fā)送網(wǎng)絡(luò)請(qǐng)求獲取DOM const cheerio = require('cheerio'); //能夠像Jquery一樣方便獲取DOM節(jié)點(diǎn) const OneUrl = "http://wufazhuce.com/"; //ONE的web版網(wǎng)站 superagent.get(OneUrl).end(function(err,res){if(err){console.log(err);}let $ = cheerio.load(res.text);let selectItem=$('#carousel-one .carousel-inner .item');let todayOne=selectItem[0]; //獲取輪播圖第一個(gè)頁面,也就是當(dāng)天更新的內(nèi)容let todayOneData={ //保存到一個(gè)json中imgUrl:$(todayOne).find('.fp-one-imagen').attr('src'),type:$(todayOne).find('.fp-one-imagen-footer').text().replace(/(^\s*)|(\s*$)/g, ""),text:$(todayOne).find('.fp-one-cita').text().replace(/(^\s*)|(\s*$)/g, "")};console.log(todayOneData); })EJS 模版引擎生成 HTML
通過爬蟲獲取到了數(shù)據(jù),那么我們就能夠通過將date輸入到EJS渲染出HTML,我們?cè)谀夸浵聞?chuàng)建js腳本和ejs模版文件:
app.js
mail.ejs
用 Node 發(fā)送郵件
這里我們可以發(fā)送純 text 也可以發(fā)送 html,注意的是郵箱密碼不是你登錄郵箱的密碼,而是 smtp 授權(quán)碼,什么是 smtp 授權(quán)碼呢?就是你的郵箱賬號(hào)可以使用這個(gè) smtp 授權(quán)碼在別的地方發(fā)郵件,一般 smtp 授權(quán)碼在郵箱官網(wǎng)的設(shè)置中可以看的到,設(shè)置如下注釋。
const nodemailer = require('nodemailer'); //發(fā)送郵件的node插件 let transporter = nodemailer.createTransport({service: '126', // 發(fā)送者的郵箱廠商,支持列表:https://nodemailer.com/smtp/well-known/port: 465, // SMTP 端口secureConnection: true, // SSL安全鏈接auth: { //發(fā)送者的賬戶密碼user: '賬戶@126.com', //賬戶pass: 'smtp授權(quán)碼', //smtp授權(quán)碼,到郵箱設(shè)置下獲取}}); let mailOptions = {from: '"發(fā)送者昵稱" <地址@126.com>', // 發(fā)送者昵稱和地址to: 'like@vince.studio', // 接收者的郵箱地址subject: '一封暖暖的小郵件', // 郵件主題text: 'test mail', //郵件的text// html: html //也可以用html發(fā)送 }; //發(fā)送郵件 transporter.sendMail(mailOptions, (error, info) => { if (error) {return console.log(error);}console.log('郵件發(fā)送成功 ID:', info.messageId); });Node 定時(shí)執(zhí)行任務(wù)
這里我們用到了 node-schedule來定時(shí)執(zhí)行任務(wù),示例如下:
var schedule = require("node-schedule"); //1. 確定的時(shí)間執(zhí)行 var date = new Date(2017,12,10,15,50,0); schedule.scheduleJob(date, function(){ console.log("執(zhí)行任務(wù)"); }); //2. 秒為單位執(zhí)行 //比如:每5秒執(zhí)行一次 var rule1 = new schedule.RecurrenceRule(); var times1 = [1,6,11,16,21,26,31,36,41,46,51,56]; rule1.second = times1; schedule.scheduleJob(rule1, function(){console.log("執(zhí)行任務(wù)"); }); //3.以分為單位執(zhí)行 //比如:每5分種執(zhí)行一次 var rule2 = new schedule.RecurrenceRule(); var times2 = [1,6,11,16,21,26,31,36,41,46,51,56]; rule2.minute = times2; schedule.scheduleJob(rule2, function(){ console.log("執(zhí)行任務(wù)"); }); //4.以天單位執(zhí)行 //比如:每天6點(diǎn)30分執(zhí)行 var rule = new schedule.RecurrenceRule(); rule.dayOfWeek = [0, new schedule.Range(1, 6)]; rule.hour = 6; rule.minute =30; var j = schedule.scheduleJob(rule, function(){console.log("執(zhí)行任務(wù)");getData(); });思路與步驟
當(dāng)所有的問題都解決后,便是開始結(jié)合代碼成一段完整的程序,思路很簡單,我們來逐步分析: 1. 由于獲取數(shù)據(jù)是異步的,并且不能判斷出哪個(gè)先獲取到數(shù)據(jù),這個(gè)是可以將獲取數(shù)據(jù)的函數(shù)封裝成一個(gè)Promise對(duì)象,最后在一起用 Promise.all 來判斷所有數(shù)據(jù)獲取完畢,再發(fā)送郵件
// 其中一個(gè)數(shù)據(jù)獲取函數(shù),其他的也是類似 function getOneData(){let p = new Promise(function(resolve,reject){superagent.get(OneUrl).end(function(err, res) {if (err) {reject(err);}let $ = cheerio.load(res.text);let selectItem = $("#carousel-one .carousel-inner .item");let todayOne = selectItem[0];let todayOneData = {imgUrl: $(todayOne).find(".fp-one-imagen").attr("src"),type: $(todayOne).find(".fp-one-imagen-footer").text().replace(/(^\s*)|(\s*$)/g, ""),text: $(todayOne).find(".fp-one-cita").text().replace(/(^\s*)|(\s*$)/g, "")};resolve(todayOneData)});})return p }2. 將爬取數(shù)據(jù)統(tǒng)一處理,作為 EJS 的參數(shù),發(fā)送郵件模板。
function getAllDataAndSendMail(){let HtmlData = {};// how long withlet today = new Date();let initDay = new Date(startDay);let lastDay = Math.floor((today - initDay) / 1000 / 60 / 60 / 24);let todaystr =today.getFullYear() +" / " +(today.getMonth() + 1) +" / " +today.getDate();HtmlData["lastDay"] = lastDay;HtmlData["todaystr"] = todaystr;Promise.all([getOneData(),getWeatherTips(),getWeatherData()]).then(function(data){HtmlData["todayOneData"] = data[0];HtmlData["weatherTip"] = data[1];HtmlData["threeDaysData"] = data[2];sendMail(HtmlData)}).catch(function(err){getAllDataAndSendMail() //再次獲取console.log('獲取數(shù)據(jù)失敗: ',err);}) }3. 發(fā)送郵件具體代碼
function sendMail(HtmlData) {const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, "email.ejs"), "utf8"));const html = template(HtmlData);let transporter = nodemailer.createTransport({service: EmianService,port: 465,secureConnection: true,auth: EamilAuth});let mailOptions = {from: EmailFrom,to: EmailTo,subject: EmailSubject,html: html};transporter.sendMail(mailOptions, (error, info={}) => {if (error) {console.log(error);sendMail(HtmlData); //再次發(fā)送}console.log("Message sent: %s", info.messageId);});}安裝與使用
如果你覺得這封郵件的內(nèi)容適合你發(fā)送的對(duì)象,可以按照以下步驟,改少量參數(shù)即可運(yùn)行程序;
git clone https://github.com/Vincedream/NodeMail
打開 main.js,修改配置項(xiàng)
終端輸入?npm install安裝依賴,再輸入?node main.js,運(yùn)行腳本,當(dāng)然你的電腦不可能不休眠,建議你部署到你的云服務(wù)器上運(yùn)行。
最后
冬天到了,是不是也該用程序員的專業(yè)知識(shí)給身邊的人帶來一些溫暖呢,源代碼與 demo 已經(jīng)放到 github上,要不試一試?
GitHub:https://github.com/Vincedream/NodeMail
你是風(fēng)兒我是沙,點(diǎn)點(diǎn)"在看"不加班
總結(jié)
以上是生活随笔為你收集整理的教你撩妹,每天给小姐姐发一封暖心邮件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 地图位置开发解析(一)
- 下一篇: 百度飞桨第一课--让人拍案叫绝的创意都是