說出來你可能不信,此次的甲方是一隻貓

背景

雙11的時候,爲了給我家主子囤些貓砂貓罐頭啥的,一直刷豆瓣愛貓小組的開車貼,邊刷邊玩cod16,結果就是錯過了不少豪車,遊戲KD也降了很多。過後我痛定思痛,把貓送人,耽擱我刷金槍 決定整個小玩意兒糊弄一下主子html

實現

其實要作的事也簡單,寫個爬蟲,定時爬一下帶關鍵字的帖子,而後郵件通知我。儘管是隨便發揮,接觸下實際工做中沒太接觸過的東西也是好的麼。
做爲一個純前端,固然是用node啦,配合puppeteer、nodemailer、node-schedule來實現核心功能前端

部分代碼

我把關鍵的部分提出來方便看,其實都是幾個工具的快速入門部分,難度基本沒有,畢竟是糊弄入門學習麼。
不合理的地方大佬們使勁噴,我頂得住node

爬蟲部分
const puppeteer = require('puppeteer');
const url = 'https://www.douban.com/group/search?group=656297&cat=1013&q=開車';

const sleep = time => new Promise(resolve => {
  setTimeout(resolve, time);
});

//  puppeteer文檔地址:https://github.com/puppeteer/puppeteer/blob/v2.0.0/docs/api.md#pagegotourl-options
const queryInfos = async () => {
  const browser = await puppeteer.launch({});

  const page = await browser.newPage();

  page.goto(url, {
    waitUntil: 'networkidle2',
  });

  await sleep(5000);//  確保爬到東西,時間能夠縮短

  const result = await page.evaluate(() => {
    // eslint-disable-next-line no-undef
    const $ = window.$;
    //  我比較關注的關鍵字,能夠考慮擴展下來實現相似只匹配貓砂但不匹配貓砂盆,這就不展開了
    const keywords = /罐|餐盒|巔峯|渴望|go|砂/; 
    const postList = $('.td-subject a');
    const links = [];

    if (postList.length > 0) {
      postList.each((i, e) => {
        const title = $(e).text();
        const link = $(e).attr('href');
        const id = link.match(/\d+/g).toString();
        if (keywords.test(title)) {
          links.push({
            id,
            title,
            link,
            keywords: title.match(keywords),
          });
        }
      });
    }

    return links;
  });

  browser.close();
  return result;
};
郵件通知
const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');

const url = 'https://www.douban.com/group/search?group=656297&cat=1013&q=開車';

const qqTransport = nodemailer.createTransport(smtpTransport({
  service: 'QQ',
  auth: {
    user: '123@qq.com',//  發件地址
    pass: '',//  受權碼
  },
}));

const sendMail = function(num, content) {
  qqTransport.sendMail({
    from: '123@qq.com',
    to: '456@qq.com',
    subject: `${num}輛新車上路`,
    html: content,
  }, function(err, res) {
    if (err) {
      return;
    }
    console.log('發送成功', res);
  });
};

async queryCarsList() {
    const list = await getInfos();
    let total = 0;
    let carList = '';
    //  注意async/await跟for更配哦
    for (let i = 0, len = list.length; i < len; i++) {
      const car = list[i];
      const {
        id,
        title,
        link,
      } = car;
      //  這部分的實現比較簡單粗暴,但願你們多批評
      //  爬到的數據我一開始簡單粗暴的直接用fs寫到json文件裏了,但來都來了,順便用下數據庫也無傷大雅麼
      const temp = await queryById(id);
      if (temp.length < 1) {
        carList += `<p><a href="${link}">${title}</a></p>`;
        total++;
        const info = new this.ctx.model.Cars({
          ...car,
        });
        info.save();
      }
    }
    if (total > 0) {
      sendMail(total, carList);
    }
}

async queryById(id) {
    return this.ctx.model.Cars.find({id});
}
定時任務

基本的功能setInterval應該也是能夠完成的,但爲了後期好擴展,並且,來都來了,試一下node-schedule也無妨git

const schedule = require('node-schedule');
const queryTask = ()=>{
    //  半小時執行一次
    schedule.scheduleJob('* 30 * * * *', queryCarsList); 
}
//  queryTask.cancel() //  取消任務

image.png

部署

部署就不展開講了,我是打算eggjs+pm2丟本身服務器上,目前仍是本機跑一下,畢竟本職工做優先級最高,並且最近不用給它囤東西了github

改進

考慮下要一直維護的話,還有不少地方要搞數據庫

  • 就這點數據量,引入數據庫是否有必要?其餘方案?
  • 開車貼時效性較高,要不要按期重置一下數據?
  • 有新車時,要不要直接爬內容,再根據內容判斷是不是關注的信息?
  • 要不要整個dashboard頁面?
  • dashboard頁面考不考慮試下WebSocket?
  • 。。。。。

算球,爲了一隻貓,逗貓棒樂呵樂呵得了❌
唉,來都來了json

總結

具體功能不難實現,但好像能接觸的東西還很多,並且要作好了仍是得花點功夫的,隨便發散一下,需求又是無限多,慢慢填吧。
對了,不合理的地方大佬們使勁噴,我頂得住
附惡毒甲方照片一張:
image.pngapi

相關文章
相關標籤/搜索