说出来你可能不信,这次的甲方是一只猫

背景

双11的时候,为了给我家主子囤些猫砂猫罐头啥的,一直刷豆瓣爱猫小组的开车贴,边刷边玩cod16,结果就是错过了很多豪车,游戏KD也降了不少。事后我痛定思痛,把猫送人,耽搁我刷金枪 决定整个小玩意儿糊弄一下主子

实现

其实要做的事也简单,写个爬虫,定时爬一下带关键字的帖子,然后邮件通知我。尽管是随便发挥,接触下实际工作中没太接触过的东西也是好的么。
作为一个纯前端,当然是用node啦,配合puppeteer、nodemailer、node-schedule来实现核心功能

部分代码

我把关键的部分提出来方便看,其实都是几个工具的快速入门部分,难度基本没有,毕竟是糊弄入门学习么。
不合理的地方大佬们使劲喷,我顶得住

爬虫部分
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: '[email protected]',//  发件地址
    pass: '',//  授权码
  },
}));

const sendMail = function(num, content) {
  qqTransport.sendMail({
    from: '[email protected]',
    to: '[email protected]',
    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 += `

${title}

`; 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也无妨

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

image.png

部署

部署就不展开讲了,我是打算eggjs+pm2丢自己服务器上,目前还是本机跑一下,毕竟本职工作优先级最高,而且最近不用给它囤东西了

改进

考虑下要一直维护的话,还有很多地方要搞

  • 就这点数据量,引入数据库是否有必要?其他方案?
  • 开车贴时效性较高,要不要定期重置一下数据?
  • 有新车时,要不要直接爬内容,再根据内容判断是否是关注的信息?
  • 要不要整个dashboard页面?
  • dashboard页面考不考虑试下WebSocket?
  • 。。。。。

算球,为了一只猫,逗猫棒乐呵乐呵得了❌
唉,来都来了

总结

具体功能不难实现,但好像能接触的东西还不少,而且要做好了还是得花点功夫的,随便发散一下,需求又是无限多,慢慢填吧。
对了,不合理的地方大佬们使劲喷,我顶得住
附恶毒甲方照片一张:
image.png

你可能感兴趣的:(node.js,javascript,puppeteer)