用Node EJS写一个爬虫剧本天天定时给亲爱的她发一封暖心邮件

本文首发于个人博客:Vince’Blog

项目源码:NodeMail,迎接star,说不定哪天脱单了就能够用到了

写在前面

自从用邮箱注册了许多账号后,便会收到诸如以下相似的邮件,刚最先还以为是一张图片,厥后细致一看不是图片呀,彷佛照样HTML呀,因而猎奇宝宝我Google一下,查阅多篇材料后总结出怎样用前端学问和Node做一个如许的“邮件网页”。

《用Node EJS写一个爬虫剧本天天定时给亲爱的她发一封暖心邮件》

确认主题

晓得怎样完胜利用后,思考着我该写什么主题呢,用一个HTML模板随便给小伙伴们发个邮件炫个技?不可,作为一个很cool的顺序员怎样能这么low呢,近来天色变化幅度大,温度捉摸不定,女朋友老是埋怨穿少了又冷穿多了又热,嗨呀,要不我就写个天天定时给宝宝发送天色预报的邮件,别的想起宝宝喜好看ONE·一个这个APP上的逐日更新,要不发天色预报的同时,再附赠一个“ONE的逐日定阅”?机灵又浪漫,最先搬砖~

剧透

本来是想末了放效果图的,怕你们看到一半就没兴趣了,就在前面剧透一下我末了做出来的效果图吧~

《用Node EJS写一个爬虫剧本天天定时给亲爱的她发一封暖心邮件》

待处置惩罚的题目

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是一个有着种种设置的定时使命发生器,能够定时每月、每一个星期、天天详细什么时候实行什么使命,这正相符天天清晨定时给宝宝发送邮件的需求。

统统准备就绪,最先做一次浪漫的顺序员

编写代码

网页爬虫

这里我们运用到superagentcheerio组合来完成爬虫:

  • 剖析网页DOM构造,以下图所示:

《用Node EJS写一个爬虫剧本天天定时给亲爱的她发一封暖心邮件》

  • 用superagent来猎取指定网页的一切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的代码,天色预报网页也是一个原理:

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
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)
  • mail.ejs
<!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>

用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
}
  1. 将爬取数据一致处置惩罚,作为EJS的参数,发送邮件模板。

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);
    })
}
  1. 发送邮件详细代码

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);
    });
  }

装置与运用

假如你以为这封邮件的内容合适你发送的对象,能够根据以下步骤,改少许参数即可运转顺序;

  1. git clone https://github.com/Vincedream…
  2. 翻开main.js,修正设置项
//纪念日
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;
  1. 终端输入npm install装置依靠,再输入node main.js,运转剧本,固然你的电脑不可能不休眠,发起你布置到你的云服务器上运转。

末了

冬季到了,是否是也该用顺序员的专业学问给身旁的人带来一些暖和呢,源代码与demo已放到github上,要不试一试?

GitHub:https://github.com/Vincedream/NodeMail

    原文作者:vincehua
    原文地址: https://segmentfault.com/a/1190000018320436
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞