教你撩妹,每天给小姐姐发一封暖心邮件
写在前面
自从用邮箱注册了很多账号后,便会收到诸如以下类似的邮件,刚开始还以为是一张图片,后来仔细一看不是图片呀,好像还是 HTML 呀,于是好奇宝宝我 Google 一下,查阅多篇资料后总结出怎么用前端知识和 Node 做一个这样的“邮件网页”。
确认主题
知道怎么实现功能后,思考着我该写什么主题呢,用一个 HTML 模板随便给小伙伴们发个邮件炫个技?不行,作为一个很 cool 的程序员怎么能这么 low 呢,最近天气变化幅度大,温度捉摸不定,女朋友总是抱怨穿少了又冷穿多了又热,嗨呀,要不我就写个每天定时给宝宝发送天气预报的邮件,另外想起宝宝喜欢看 ONE·一个这个APP上的每日更新,要不发天气预报的同时,再附赠一个“ONE 的每日订阅”?机智又浪漫,开始搬砖~
剧透
本来是想最后放效果图的,怕你们看到一半就没兴趣了,就在前面剧透一下我最后做出来的效果图吧~
待解决的问题
1. 如何获取天气预报和 ONE 上的 data?
答:获取data有两种方法,第一种方法是获取天气预报和 ONE 的 API,第二种是用 node 爬虫获取天气预报和 ONE 网页的信息。后来找了下,发现 ONE 并没有 API 接口,为了让两者统一,于是决定使用 node 上的一个插件叫 cheerio,配合 superagent能够很方便地爬取网页上的信息。
2. 如何做出 HTML 的这种邮件?
答:之前学过一段时间的 express 这个框架,接触到模版引擎这个概念,传入data 便可获得 html 文件,再结合 node 的 fs 模块,获取到这个 html 文件,便可以结合 node 的邮件插件发送 HTML 邮件啦!
3. 如何用 node 发送邮件?
感谢无私的开源开发者,开发了一款发送邮件的 Node 插件 nodemailer,兼容主流的 Email 厂商,只需要配置好邮箱账号和 smtp 授权码,便可以用你的邮箱账号在 node 脚本上发文件,很 cool 有没有~
4. 如何做到每日定时发送?
其实可以通过各种 hack 的方式写这么一个定时任务,但是既然 node 社区有这个定时的轮子,那我们直接用就好了, node-schedule是一个有着各种配置的定时任务发生器,可以定时每个月、每个礼拜、每天具体什么时候执行什么任务,这正符合每天早晨定时给宝宝发送邮件的需求。
一切准备就绪,开始做一次浪漫的程序员
编写代码
网页爬虫
这里我们使用到 superagent和 cheerio组合来实现爬虫:
分析网页 DOM 结构,如下图所示:
用 superagent 来获取指定网页的所有 DOM:
用 cheerio 来筛选 superagent 获取到的 DOM,取出需要的 DOM
以下就是爬取 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); })EJS 模版引擎生成 HTML
通过爬虫获取到了数据,那么我们就能够通过将date输入到EJS渲染出HTML,我们在目录下创建js脚本和ejs模版文件:
app.js
mail.ejs
用 Node 发送邮件
这里我们可以发送纯 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 定时执行任务
这里我们用到了 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(); });思路与步骤
当所有的问题都解决后,便是开始结合代码成一段完整的程序,思路很简单,我们来逐步分析: 1. 由于获取数据是异步的,并且不能判断出哪个先获取到数据,这个是可以将获取数据的函数封装成一个Promise对象,最后在一起用 Promise.all 来判断所有数据获取完毕,再发送邮件
// 其中一个数据获取函数,其他的也是类似 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. 将爬取数据统一处理,作为 EJS 的参数,发送邮件模板。
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('获取数据失败: ',err);}) }3. 发送邮件具体代码
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);});}安装与使用
如果你觉得这封邮件的内容适合你发送的对象,可以按照以下步骤,改少量参数即可运行程序;
git clone https://github.com/Vincedream/NodeMail
打开 main.js,修改配置项
终端输入 npm install安装依赖,再输入 node main.js,运行脚本,当然你的电脑不可能不休眠,建议你部署到你的云服务器上运行。
最后
冬天到了,是不是也该用程序员的专业知识给身边的人带来一些温暖呢,源代码与 demo 已经放到 github上,要不试一试?
GitHub:https://github.com/Vincedream/NodeMail
你是风儿我是沙,点点"在看"不加班
总结
以上是生活随笔为你收集整理的教你撩妹,每天给小姐姐发一封暖心邮件的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 地图位置开发解析(一)
- 下一篇: 百度飞桨第一课--让人拍案叫绝的创意都是