node.js爬取資料並定時傳送HTML郵件

Vadim發表於2019-03-26

  node.js是前端程式設計師不可不學的一個框架,我們可以通過它來爬取資料、傳送郵件、存取資料等等。下面我們通過koa2框架簡單的只有一個小爬蟲並使用定時任務來傳送小郵件!

  首先我們先來看一下效果圖

 

  差不多就是這樣,其實之前已經有人做了類似的東西,我也只是想自己操作一遍,練習一下koa2框架,async+await,以及爬蟲、定時器和傳送郵件。下面我將帶著各位剛剛學習node的小童鞋進入這個世界。

1.我們先來看一看這個專案用到的框架和依賴

  1. koa2框架--基於Node.js平臺的下一代web開發框架,也就是我們的開發載體
  2. ejs--嵌入式JavaScript模板引擎
  3. superagent--客戶端請求代理模組
  4. cheerio--抓取網頁資料模組
  5. node-schedule--任務排程器模組(定時器)
  6. nodemailer--郵件傳送模組

  差不多就是這些模組,首先來初始化koa2專案,並進入目錄

npm install -g koa-generator
koa2 test_koa
cd test_koa

  然後安裝各種依賴

npm install ejs superagent cheerio node-schedule nodemailer --save

2.編輯ejs模板

  因為我們要展示最近三天的編號、天氣、溫度、汙染程度、以及one網站的圖片、圖片來源和雞湯。所以按照我們想要表現的資料編寫ejs模板。這裡我們留下待使用的資料介面,想要詳細學習請點選ejs官方網站

3.接下來我們編寫util工具方法集

  首先引入方法需要的依賴

const cheerio = require('cheerio');
const superagent = require('superagent');
const nodemailer = require('nodemailer');

  具體是做什麼的上面已經提及

1.首先編寫爬蟲爬取one網站的頁面,然後獲取第一張圖片也就是今天的圖片。

module.exports.getOneData = async (url) => {
  return new Promise( resolve => {
    superagent.get(url).end((err,res)=>{
      if(err){
        return 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)
    })
  })
}

  因為superagent.get(url).end方法是一個非同步的方法,所以我們使用async方法返回一個Promise物件,superagent.get(url)中的url為客戶端請求代理模組路徑。end方法中有兩個引數,第一返回錯誤,第二個是獲取的網頁結果物件,res.text就是網頁原始碼。接下來使用cheerio模組轉化整個網頁原始碼成jquery模式。

let $ = cheerio.load(res.text);

  之後分別獲取圖片路徑、圖片來源和雞湯文

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,""),

  並封裝成物件,.replace(/(^\s*)|(\s*$)/g,"")是清除文字的前後導空格。

2.然後爬取天氣預報網站的頁面。

  這個原理和上面的一樣就不多做解釋,直接貼程式碼

module.exports.getWeatherData = async (url) => {
  return new Promise( resolve => {
    superagent.get(url).end((err,res)=>{
      if(err){
        return err
      }
      let $ = cheerio.load(res.text);
      let arr = [];
      $('.table_day7').each((index,item)=>{
        if (index < 3) {
          arr.push({
            day: $(item).find('dd:nth-of-type(1)').text(),
            air: $(item).find('dd:nth-of-type(2) b').text(),
            icon: "http:"+$(item).find('dd:nth-of-type(3) img').attr("src"),
            weather: $(item).find('dd:nth-of-type(4)').text(),
            temp: $(item).find('dd:nth-of-type(5)').text().replace(/℃/g,"°"),
          })
        }
      })
      resolve(arr)
    })
  })
}

3.接下來我們編寫方法,傳送email的天數

module.exports.getDateIndex = () => Math.ceil((new Date().getTime() - 1553085879604) / ( 24 * 60 * 60 *1000 ));

  先生成一個當天的時間戳,我這裡是1553085879604,然後通過這個時間戳計算是第幾天傳送資訊,這個很簡單。

4.當天的日期

  這個也不用說,直接貼程式碼

module.exports.getToday = () => new Date().getFullYear() + " / " + new Date().getMonth() + " / " + new Date().getDate();

5.我們使用nodemailer模組傳送

module.exports.sendEmail = (html) => {
  nodemailer.createTestAccount(() => {
    let transporter = nodemailer.createTransport({
      service: 'qq',
      port: 568,
      secure: false,
      auth: {
        user: '1149967915@qq.com',
        pass: '這裡填寫你自己的SMTP授權碼'
      }
    });
    let mailOptions = {
      from: '"郭志強" <1149967915@qq.com>',
      to: '15045160109@163.com',
      subject: '一碗雞湯趁熱喝!',
      html: html
    };
    transporter.sendMail(mailOptions, (error, info) => {
      if (error) {
        return console.log(error);
      }
      console.log('傳送成功----', info.accepted[0], new Date());
    });
  });
}

  這裡需要注意的是auth的pass不是密碼,而是SMTP授權碼,想知道怎麼得到SMTP授權碼的點這裡。上面這五個方法基本就夠了。

4.在專案根目錄下建立schedule.js並呼叫定時器方法

  這裡我們寫一個定時器並且呼叫上面寫的方法,首先引入依賴

const schedule = require('node-schedule');
const fs = require('fs');
const path = require('path');
const ejs = require('ejs');

  接下來引入util工具庫中的方法

const { getOneData, getWeatherData, getDateIndex, getToday, sendEmail } = require('./util/index');

  在呼叫getOneData方法和getWeatherData方法時。

const oneUrl = "http://wufazhuce.com";
const weatherUrl = "http://www.tianqi.com/beijing/7/";
const oneData = await getOneData(oneUrl);
const weatherData= await getWeatherData(weatherUrl);

  像上面這樣寫顯然是不合理的,我們並不需要等待getOneData方法執行完才去執行getWeatherData。node社群從不會辜負我們,我們可以採取await Promise.all方法同時執行這兩個非同步方法。之後是獲取ejs模板,並通過介面渲染資料。

const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, 'views/index.ejs'), 'utf8'));

  最後呼叫sendEmail方法傳送郵件。

sendEmail(html)

  schedule定時任務模組就不多講,詳情請看點選這裡,下面貼上schedule.js所有程式碼

const schedule = require('node-schedule');
const fs = require('fs');
const path = require('path');
const ejs = require('ejs');

const { getOneData, getWeatherData, getDateIndex, getToday, sendEmail } = require('./util/index');

module.exports = function () {
  //定時任務
  schedule.scheduleJob('0 0 7 * * *', async () => {
    const oneUrl = "http://wufazhuce.com";
    const weatherUrl = "http://www.tianqi.com/beijing/7/";
    const [oneData, weatherData] = await Promise.all([getOneData(oneUrl), getWeatherData(weatherUrl)]);
    const dateIndex = getDateIndex();
    const today = getToday();
    const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, 'views/index.ejs'), 'utf8'));
    let ejsModelObject = {
      oneData: oneData,
      weatherData: weatherData,
      dateIndex: dateIndex,
      today: today,
    }
    const html = template(ejsModelObject);
    sendEmail(html)
  });
}

  到這裡就可以收工了,你也可以給心愛人送上暖心的郵件,這就自由發揮了。感謝大家支援,喜歡就收藏吧。部落格園同步更新。

 

原創部落格:轉載請註明node.js爬取資料並定時傳送HTML郵件

 

 

 

相關文章