本文首發於我的博客:Vince'Blogjavascript
項目源碼:NodeMail,歡迎star,說不定哪天脫單了就能用到了html
自從用郵箱註冊了不少帳號後,便會收到諸如如下相似的郵件,剛開始還覺得是一張圖片,後來仔細一看不是圖片呀,好像仍是HTML呀,因而好奇寶寶我Google一下,查閱多篇資料後總結出怎麼用前端知識和Node作一個這樣的「郵件網頁」。前端
知道怎麼實現功能後,思考着我該寫什麼主題呢,用一個HTML模板隨便給小夥伴們發個郵件炫個技?不行,做爲一個很cool的程序員怎麼能這麼low呢,最近天氣變化幅度大,溫度捉摸不定,女友老是抱怨穿少了又冷穿多了又熱,嗨呀,要不我就寫個天天定時給寶寶發送天氣預報的郵件,另外想起寶寶喜歡看ONE·一個這個APP上的每日更新,要不發天氣預報的同時,再附贈一個「ONE的每日訂閱」?機智又浪漫,開始搬磚~java
原本是想最後放效果圖的,怕大家看到一半就沒興趣了,就在前面劇透一下我最後作出來的效果圖吧~node
1. 如何獲取天氣預報和ONE上的data?git
答:獲取data有兩種方法,第一種方法是獲取天氣預報和ONE的API,第二種是用node爬蟲獲取天氣預報和ONE網頁的信息。後來找了下,發現ONE並無API接口,爲了讓二者統一,因而決定使用node上的一個插件叫cheerio
,配合superagent
可以很方便地爬取網頁上的信息。程序員
2. 如何作出HTML的這種郵件?github
答:以前學過一段時間的express這個框架,接觸到模版引擎這個概念,傳入data即可得到html文件,再結合node的fs模塊,獲取到這個html文件,即可以結合node的郵件插件發送HTML郵件啦!web
3. 如何用node發送郵件?express
感謝無私的開源開發者,開發了一款發送郵件的Node插件nodemailer
,兼容主流的Email廠商,只須要配置好郵箱帳號和smtp受權碼,即可以用你的郵箱帳號在node腳本上發文件,很cool有沒有~
4. 如何作到每日定時發送?
其實能夠經過各類hack的方式寫這麼一個定時任務,可是既然node社區有這個定時的輪子,那咱們直接用就行了,node-schedule
是一個有着各類配置的定時任務發生器,能夠定時每月、每一個禮拜、天天具體何時執行什麼任務,這正符合天天早晨定時給寶寶發送郵件的需求。
一切準備就緒,開始作一次浪漫的程序員
這裏咱們使用到superagent
和cheerio
組合來實現爬蟲:
superagent.get(URL).end(function(err,res){ // }
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, "")
如下就是爬取ONE的代碼,天氣預報網頁也是一個道理:
const superagent = require('superagent'); //發送網絡請求獲取DOM const cheerio = require('cheerio'); //可以像Jquery同樣方便獲取DOM節點 const OneUrl = "http://wufazhuce.com/"; //ONE的web版網站 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]; //獲取輪播圖第一個頁面,也就是當天更新的內容 let todayOneData={ //保存到一個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); })
經過爬蟲獲取到了數據,那麼咱們就可以經過將date輸入到EJS渲染出HTML,咱們在目錄下建立js腳本和ejs模版文件:
const ejs = require('ejs'); //ejs模版引擎 const fs = require('fs'); //文件讀寫 const path = require('path'); //路徑配置 //傳給EJS的數據 let data={ title:'nice to meet you~' } //將目錄下的mail.ejs獲取到,獲得一個模版 const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, 'mail.ejs'), 'utf8')); //將數據傳入模版中,生成HTML const html = template(data); console.log(html)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <h1> <%= title %> </h1> </body> </html>
這裏咱們能夠發送純text也能夠發送html,注意的是郵箱密碼不是你登陸郵箱的密碼,而是smtp受權碼,什麼是smtp受權碼呢?就是你的郵箱帳號可使用這個smtp受權碼在別的地方發郵件,通常smtp受權碼在郵箱官網的設置中能夠看的到,設置以下注釋。
const nodemailer = require('nodemailer'); //發送郵件的node插件 let transporter = nodemailer.createTransport({ service: '126', // 發送者的郵箱廠商,支持列表:https://nodemailer.com/smtp/well-known/ port: 465, // SMTP 端口 secureConnection: true, // SSL安全連接 auth: { //發送者的帳戶密碼 user: '帳戶@126.com', //帳戶 pass: 'smtp受權碼', //smtp受權碼,到郵箱設置下獲取 } }); let mailOptions = { from: '"發送者暱稱" <地址@126.com>', // 發送者暱稱和地址 to: 'like@vince.studio', // 接收者的郵箱地址 subject: '一封暖暖的小郵件', // 郵件主題 text: 'test mail', //郵件的text // html: html //也能夠用html發送 }; //發送郵件 transporter.sendMail(mailOptions, (error, info) => { if (error) { return console.log(error); } console.log('郵件發送成功 ID:', info.messageId); });
這裏咱們用到了node-schedule
來定時執行任務,示例以下:
var schedule = require("node-schedule"); //1. 肯定的時間執行 var date = new Date(2017,12,10,15,50,0); schedule.scheduleJob(date, function(){ console.log("執行任務"); }); //2. 秒爲單位執行 //好比:每5秒執行一次 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("執行任務"); }); //3.以分爲單位執行 //好比:每5分種執行一次 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("執行任務"); }); //4.以天單位執行 //好比:天天6點30分執行 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("執行任務"); getData(); });
當全部的問題都解決後,即是開始結合代碼成一段完整的程序,思路很簡單,咱們來逐步分析:
// 其中一個數據獲取函數,其餘的也是相似 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 }
function getAllDataAndSendMail(){ let HtmlData = {}; // how long with let 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('獲取數據失敗: ',err); }) }
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); //再次發送 } console.log("Message sent: %s", info.messageId); }); }
若是你以爲這封郵件的內容適合你發送的對象,能夠按照如下步驟,改少許參數便可運行程序;
//記念日 let startDay = "2016/6/24"; //當地拼音,須要在下面的墨跡天氣url確認 const local = "xiangtan"; //發送者郵箱廠家 let EmianService = "163"; //發送者郵箱帳戶SMTP受權碼 let EamilAuth = { user: "xxxxxx@163.com", pass: "xxxxxx" }; //發送者暱稱與郵箱地址 let EmailFrom = '"name" <xxxxxx@163.com>'; //接收者郵箱地 let EmailTo = "like@vince.studio"; //郵件主題 let EmailSubject = "一封暖暖的小郵件"; //每日發送時間 let EmailHour = 6; let EmialMinminute= 30;
npm install
安裝依賴,再輸入node main.js
,運行腳本,固然你的電腦不可能不休眠,建議你部署到你的雲服務器上運行。冬天到了,是否是也該用程序員的專業知識給身邊的人帶來一些溫暖呢,源代碼與demo已經放到github上,要不試一試?