摘要:本文首發(fā)于個人博客項目源碼,歡迎,說不定哪天脫單了就能用到了寫在前面自從用郵箱注冊了很多賬號后,便會收到諸如以下類似的郵件剛開始還以為是一張圖片,后來仔細一看不是圖片呀,好像還是呀,于是好奇寶寶我一下,查閱多篇資料后總結(jié)出怎么用前端知識和做
本文首發(fā)于個人博客:Vince"Blog
項目源碼:NodeMail,歡迎star,說不定哪天脫單了就能用到了
寫在前面自從用郵箱注冊了很多賬號后,便會收到諸如以下類似的郵件,剛開始還以為是一張圖片,后來仔細一看不是圖片呀,好像還是HTML呀,于是好奇寶寶我Google一下,查閱多篇資料后總結(jié)出怎么用前端知識和Node做一個這樣的“郵件網(wǎng)頁”。
確認主題知道怎么實現(xiàn)功能后,思考著我該寫什么主題呢,用一個HTML模板隨便給小伙伴們發(fā)個郵件炫個技?不行,作為一個很cool的程序員怎么能這么low呢,最近天氣變化幅度大,溫度捉摸不定,女朋友總是抱怨穿少了又冷穿多了又熱,嗨呀,要不我就寫個每天定時給寶寶發(fā)送天氣預(yù)報的郵件,另外想起寶寶喜歡看ONE·一個這個APP上的每日更新,要不發(fā)天氣預(yù)報的同時,再附贈一個“ONE的每日訂閱”?機智又浪漫,開始搬磚~
劇透本來是想最后放效果圖的,怕你們看到一半就沒興趣了,就在前面劇透一下我最后做出來的效果圖吧~
待解決的問題1. 如何獲取天氣預(yù)報和ONE上的data?
答:獲取data有兩種方法,第一種方法是獲取天氣預(yù)報和ONE的API,第二種是用node爬蟲獲取天氣預(yù)報和ONE網(wǎng)頁的信息。后來找了下,發(fā)現(xiàn)ONE并沒有API接口,為了讓兩者統(tǒng)一,于是決定使用node上的一個插件叫cheerio,配合superagent能夠很方便地爬取網(wǎng)頁上的信息。
2. 如何做出HTML的這種郵件?
答:之前學(xué)過一段時間的express這個框架,接觸到模版引擎這個概念,傳入data便可獲得html文件,再結(jié)合node的fs模塊,獲取到這個html文件,便可以結(jié)合node的郵件插件發(fā)送HTML郵件啦!
3. 如何用node發(fā)送郵件?
感謝無私的開源開發(fā)者,開發(fā)了一款發(fā)送郵件的Node插件nodemailer,兼容主流的Email廠商,只需要配置好郵箱賬號和smtp授權(quán)碼,便可以用你的郵箱賬號在node腳本上發(fā)文件,很cool有沒有~
4. 如何做到每日定時發(fā)送?
其實可以通過各種hack的方式寫這么一個定時任務(wù),但是既然node社區(qū)有這個定時的輪子,那我們直接用就好了,node-schedule是一個有著各種配置的定時任務(wù)發(fā)生器,可以定時每個月、每個禮拜、每天具體什么時候執(zhí)行什么任務(wù),這正符合每天早晨定時給寶寶發(fā)送郵件的需求。
一切準備就緒,開始做一次浪漫的程序員
編寫代碼 網(wǎng)頁爬蟲這里我們使用到superagent和cheerio組合來實現(xiàn)爬蟲:
分析網(wǎng)頁DOM結(jié)構(gòu),如下圖所示:
用superagent來獲取指定網(wǎng)頁的所有DOM:
superagent.get(URL).end(function(err,res){ // }
用cheerio來篩選superagent獲取到的DOM,取出需要的DOM
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的代碼,天氣預(yù)報網(wǎng)頁也是一個道理:
const superagent = require("superagent"); //發(fā)送網(wǎng)絡(luò)請求獲取DOM const cheerio = require("cheerio"); //能夠像Jquery一樣方便獲取DOM節(jié)點 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]; //獲取輪播圖第一個頁面,也就是當(dāng)天更新的內(nèi)容 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); })EJS模版引擎生成HTML
通過爬蟲獲取到了數(shù)據(jù),那么我們就能夠通過將date輸入到EJS渲染出HTML,我們在目錄下創(chuàng)建js腳本和ejs模版文件:
app.js
const ejs = require("ejs"); //ejs模版引擎 const fs = require("fs"); //文件讀寫 const path = require("path"); //路徑配置 //傳給EJS的數(shù)據(jù) let data={ title:"nice to meet you~" } //將目錄下的mail.ejs獲取到,得到一個模版 const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, "mail.ejs"), "utf8")); //將數(shù)據(jù)傳入模版中,生成HTML const html = template(data); console.log(html)
mail.ejs
用Node發(fā)送郵件Document <%= title %>
這里我們可以發(fā)送純text也可以發(fā)送html,注意的是郵箱密碼不是你登錄郵箱的密碼,而是smtp授權(quán)碼,什么是smtp授權(quán)碼呢?就是你的郵箱賬號可以使用這個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: "[email protected]", // 接收者的郵箱地址 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定時執(zhí)行任務(wù)
這里我們用到了node-schedule來定時執(zhí)行任務(wù),示例如下:
var schedule = require("node-schedule"); //1. 確定的時間執(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點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é)合代碼成一段完整的程序,思路很簡單,我們來逐步分析:
由于獲取數(shù)據(jù)是異步的,并且不能判斷出哪個先獲取到數(shù)據(jù),這個是可以將獲取數(shù)據(jù)的函數(shù)封裝成一個Promise對象,最后在一起用Promise.all來判斷所有數(shù)據(jù)獲取完畢,再發(fā)送郵件
// 其中一個數(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 }
將爬取數(shù)據(jù)統(tǒng)一處理,作為EJS的參數(shù),發(fā)送郵件模板。
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("獲取數(shù)據(jù)失?。?",err); }) }
發(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ā)送的對象,可以按照以下步驟,改少量參數(shù)即可運行程序;
git clone https://github.com/Vincedream...
打開main.js,修改配置項
//紀念日 let startDay = "2016/6/24"; //當(dāng)?shù)仄匆?需要在下面的墨跡天氣url確認 const local = "xiangtan"; //發(fā)送者郵箱廠家 let EmianService = "163"; //發(fā)送者郵箱賬戶SMTP授權(quán)碼 let EamilAuth = { user: "[email protected]", pass: "xxxxxx" }; //發(fā)送者昵稱與郵箱地址 let EmailFrom = ""name""; //接收者郵箱地 let EmailTo = "[email protected]"; //郵件主題 let EmailSubject = "一封暖暖的小郵件"; //每日發(fā)送時間 let EmailHour = 6; let EmialMinminute= 30;
終端輸入npm install安裝依賴,再輸入node main.js,運行腳本,當(dāng)然你的電腦不可能不休眠,建議你部署到你的云服務(wù)器上運行。
最后冬天到了,是不是也該用程序員的專業(yè)知識給身邊的人帶來一些溫暖呢,源代碼與demo已經(jīng)放到github上,要不試一試?
GitHub:https://github.com/Vincedream/NodeMail
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102170.html
摘要:但是,畢竟是人,哪天忙了就會忘記提交,所以想著能不能實現(xiàn)在自己阿里云服務(wù)器系統(tǒng)上,設(shè)置,定制下命令,實現(xiàn)每天定點自動提交。 前言 進入自己github主頁會看到自己的提交記錄,如果某天沒有提交記錄,那天的小方框就顯示灰色。強迫癥的我,每次進來看著就感覺不爽,想著自己每天記得提交點東西,爭取像阮一峰大神一樣,每天都有提交記錄。 showImg(https://www.wty90.co...
摘要:使用腳本發(fā)送郵件并不復(fù)雜。以下為思路導(dǎo)圖模塊與發(fā)送郵件相關(guān)的模塊是關(guān)于簡單郵件傳輸協(xié)議的操作模塊,在發(fā)送郵件的過程中起到服務(wù)器之間互相通信的作用。 0. 前言 發(fā)送電子郵件是個很常見的開發(fā)需求。比如你寫了個監(jiān)控天氣的腳本,發(fā)現(xiàn)第二天要下雨,或者網(wǎng)站上關(guān)注的某個商品降價了,就可以發(fā)個郵件到郵箱來提醒自己。 使用 Python 腳本發(fā)送郵件并不復(fù)雜。不過由于各家郵件的發(fā)送機制和安全策略不同...
摘要:也就是說,我的篇文章的請求對應(yīng)個實例,這些實例都請求完畢后,執(zhí)行以下邏輯他的目的在于對每一個返回值這個返回值為單篇文章的內(nèi)容,進行方法處理。 英國人Robert Pitt曾在Github上公布了他的爬蟲腳本,導(dǎo)致任何人都可以容易地取得Google Plus的大量公開用戶的ID信息。至今大概有2億2千5百萬用戶ID遭曝光。 亮點在于,這是個nodejs腳本,非常短,包括注釋只有71行。 ...
摘要:源碼地址準備一臺云服務(wù)器寫好的腳本效果因為現(xiàn)在一個的客戶端啟動越來越慢,而且很多自己不感興趣的東西我只是想看看文章,所以就寫了這個小爬蟲。因為一個是每天點會更新,所以自己的服務(wù)器要做一個定時服務(wù),下自帶了定時任務(wù)。 源碼地址:https://github.com/xcc3641/pySendOneToEmail 準備 一臺云服務(wù)器 寫好的Python腳本 效果 因為現(xiàn)在一個的And...
閱讀 2189·2020-06-12 14:26
閱讀 2492·2019-08-29 16:41
閱讀 1890·2019-08-29 15:28
閱讀 2459·2019-08-26 13:43
閱讀 759·2019-08-26 13:37
閱讀 2781·2019-08-23 18:13
閱讀 2805·2019-08-23 15:31
閱讀 1022·2019-08-23 14:10