资讯专栏INFORMATION COLUMN

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

zero / 2774人阅读

摘要:本文首发于个人博客项目源码,欢迎,说不定哪天脱单了就能用到了写在前面自从用邮箱注册了很多账号后,便会收到诸如以下类似的邮件刚开始还以为是一张图片,后来仔细一看不是图片呀,好像还是呀,于是好奇宝宝我一下,查阅多篇资料后总结出怎么用前端知识和做

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

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

写在前面

自从用邮箱注册了很多账号后,便会收到诸如以下类似的邮件,刚开始还以为是一张图片,后来仔细一看不是图片呀,好像还是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是一个有着各种配置的定时任务发生器,可以定时每个月、每个礼拜、每天具体什么时候执行什么任务,这正符合每天早晨定时给宝宝发送邮件的需求。

一切准备就绪,开始做一次浪漫的程序员

编写代码 网页爬虫

这里我们使用到superagentcheerio组合来实现爬虫:

分析网页DOM结构,如下图所示:

用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




    
    
    
    Document


    

<%= title %>

用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();
});
思路与步骤

当所有的问题都解决后,便是开始结合代码成一段完整的程序,思路很简单,我们来逐步分析:

由于获取数据是异步的,并且不能判断出哪个先获取到数据,这个是可以将获取数据的函数封装成一个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
}

将爬取数据统一处理,作为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);
    })
}

发送邮件具体代码

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...

打开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" ";

//接收者邮箱地
let EmailTo = "like@vince.studio";
//邮件主题
let EmailSubject = "一封暖暖的小邮件";

//每日发送时间
let EmailHour = 6;
let EmialMinminute= 30;

终端输入npm install安装依赖,再输入node main.js,运行脚本,当然你的电脑不可能不休眠,建议你部署到你的云服务器上运行。

最后

冬天到了,是不是也该用程序员的专业知识给身边的人带来一些温暖呢,源代码与demo已经放到github上,要不试一试?

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102170.html

相关文章

  • 手把手教你刷github提交记录

    摘要:但是,毕竟是人,哪天忙了就会忘记提交,所以想着能不能实现在自己阿里云服务器系统上,设置,定制下命令,实现每天定点自动提交。 前言 进入自己github主页会看到自己的提交记录,如果某天没有提交记录,那天的小方框就显示灰色。强迫症的我,每次进来看着就感觉不爽,想着自己每天记得提交点东西,争取像阮一峰大神一样,每天都有提交记录。 showImg(https://www.wty90.co...

    ChanceWong 评论0 收藏0
  • 简单三步, Python 发邮件

    摘要:使用脚本发送邮件并不复杂。以下为思路导图模块与发送邮件相关的模块是关于简单邮件传输协议的操作模块,在发送邮件的过程中起到服务器之间互相通信的作用。 0. 前言 发送电子邮件是个很常见的开发需求。比如你写了个监控天气的脚本,发现第二天要下雨,或者网站上关注的某个商品降价了,就可以发个邮件到邮箱来提醒自己。 使用 Python 脚本发送邮件并不复杂。不过由于各家邮件的发送机制和安全策略不同...

    haobowd 评论0 收藏0
  • nodeJS实现基于Promise爬虫 定时发送信息到指定邮件

    摘要:也就是说,我的篇文章的请求对应个实例,这些实例都请求完毕后,执行以下逻辑他的目的在于对每一个返回值这个返回值为单篇文章的内容,进行方法处理。 英国人Robert Pitt曾在Github上公布了他的爬虫脚本,导致任何人都可以容易地取得Google Plus的大量公开用户的ID信息。至今大概有2亿2千5百万用户ID遭曝光。 亮点在于,这是个nodejs脚本,非常短,包括注释只有71行。 ...

    xuweijian 评论0 收藏0
  • [新手开源] 爬取韩寒“一个”文章且自动邮件发送功能

    摘要:源码地址准备一台云服务器写好的脚本效果因为现在一个的客户端启动越来越慢,而且很多自己不感兴趣的东西我只是想看看文章,所以就写了这个小爬虫。因为一个是每天点会更新,所以自己的服务器要做一个定时服务,下自带了定时任务。 源码地址:https://github.com/xcc3641/pySendOneToEmail 准备 一台云服务器 写好的Python脚本 效果 因为现在一个的And...

    zhkai 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<