iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0

https://ithelp.ithome.com.tw/upload/images/20230915/201194864f6hVBBnPo.png

前言

前一篇我們對於 Crontab 已經有一個基本的概念,接下來當然要套用到實際案例上,不然認識了也沒用,所以這一篇我們就來簡單實作一個 Crontab + Node.js 的範例。

專案環境

首先我們先來建立一個專案,前面這個指令應該是苦瓜爛熟了,所以我就不多說了:

mkdir crontab-example-nodejs
cd crontab-example-nodejs
npm init -y
touch index.js

接下來我們還要額外安裝一個套件,也就是 cron

npm i cron

CronJob

Cron 的使用方式非常的簡單,依照官方範例文件所提供的程式碼來看,只要先引入 cron 套件,接著就可以使用 CronJob 這個類別來建立一個 CronJob。

var CronJob = require('cron').CronJob;

var job = new CronJob(
    '* * * * * *',
    function() {
        console.log('You will see this message every second');
    },
    null,
    true,
    'America/Los_Angeles'
);

稍微有一點複雜吧?那我們就來一個一個解釋:

  • 第一個參數:這個參數就是剛剛我們所提到的 Crontab 格式,也就是說,這個參數就是用來設定你要執行的時間。
  • 第二個參數:這個參數就是你要執行的程式碼。
  • 第三個參數:完成排程後要執行的程式碼。
  • 第四個參數:是否立即執行。
  • 第五個參數:時區。

但實際上來講,我們比較常這樣寫

const CronJob = require('cron').CronJob;

const job = new CronJob(
    '* * * * * *',
    function() {
        console.log('You will see this message every second');
    }
);

因為三~五個參數都是選填的,所以我們可以不用寫,而第一個參數就是我們要設定的時間,第二個參數就是我們要執行的程式碼,接著最後再補上一個 job.start() 就可以了。

const CronJob = require('cron').CronJob;

const job = new CronJob(
    '* * * * * *',
    function() {
        console.log('You will see this message every second');
    }
);

job.start();

Note
由於前一篇已經有提到 Crontab 的格式,所以這邊就不再贅述,如果你還不清楚的話,可以回去看一下前一篇。

反之,如果你期望停止這個排程的話,那就可以使用 job.stop() 來停止。

搭配爬蟲

那麼你還記得我們在前面寫了兩隻爬蟲嗎?分別是:

  • 爬取鐵人賽參賽者列表
  • 爬取鐵人賽文章詳細資訊

透過 cron 的排程,我們就可以定時的去爬取資料,這樣就不用每次都要手動去執行了,而且也不用擔心忘記執行,所以我們就來實作一下吧!

首先我們先回顧一下前面寫的程式碼也就是 index.js

// index.js
const fs = require('fs');

const cheerio = require('cheerio');

const getData = async (url) => {
  try {
    const response = await fetch(url);
    const data = await response.text();
    return data;
  } catch (error) {
    console.log(error);
  }
}

const crawler = async () => {
  const html = await getData('https://ithelp.ithome.com.tw/2022ironman/signup/list');
  const $ = cheerio.load(html); // 載入 html
  const paginationInner = $('span.pagination-inner > a').last(); // 取得最後一頁的頁碼
  const lastPage = Number(paginationInner.text()); // 取得最後一頁的頁碼文字並轉成數字
  const data = [];

  // 用 for 迴圈來爬取每一頁的資料
  for(let i = 1; i <= lastPage; i++) {
    console.log(`正在爬取第 ${i} 頁`);
    const html = await getData(`https://ithelp.ithome.com.tw/2022ironman/signup/list?page=${i}`);

    const $ = cheerio.load(html); // 載入 html

    const listCard = $('.list-card');

    listCard.each((index, element) => {
      const name = $(element).find('.contestants-list__name').text();
      const category = $(element).find('.tag span').text();
      const title = $(element).find('.contestants-list__title').text();
      const url = $(element).find('.contestants-list__title').attr('href');
      data.push({
        name,
        category,
        title,
        url,
      });
    });

    // 避免過度請求增加伺服器負擔
    await new Promise((resolve) => {
      setTimeout(() => {
        resolve();
      }, 5000); // 5 秒跑一次
    })
  }

  fs.writeFileSync('./data.json', JSON.stringify(data));
}

crawler();

前面這一個程式碼就是用來爬取鐵人賽參賽者列表的,而我們要做的就是把這個程式碼改成可以定時執行的,,但其實用法很簡單,只要把程式碼包在 CronJob 裡面就可以了

// index.js
const fs = require('fs');
const CronJob = require('cron').CronJob;
const cheerio = require('cheerio');

const getData = async (url) => {
  // ... 略過程式碼
}

const crawler = async () => {
  // ... 略過程式碼
}

const job = new CronJob(
  '* * * * * *',
  function() {
    crawler();
  }
);

這樣子我們就可以達到每分鐘爬取一次的效果,但實際上來講,我們並不需要那麼頻繁的撈取資料,所以我們可以改成每個禮拜一早上九點爬取一次

// index.js

// ... 略過程式碼

const job = new CronJob(
  '0 9 * * 1',
  function() {
    crawler();
  }
);

job.start();

那麼另一個爬取鐵人賽文章詳細資訊的程式碼也是一樣的,只是這邊就留給你自己試試看了,我就不多做說明與解釋囉~

混亂知識點

現在應該會發生一點混亂點,也就是 Node.js 的 Cron 跟 Linux 的 Cron 有什麼差別呢?

https://ithelp.ithome.com.tw/upload/images/20230915/20119486rUKUwj0fIl.png

首先我們在 npm 所下載安裝的 cron 套件,主要是運行於 Node.js 環境下的,而 Linux 則是一個作業系統,所以這兩個東西是不太一樣的東西,只是概念以及時間格式是類似相同的,所以有時候會讓人有點混淆。

因此通常你會比較常看到人家講 node-cron,但只是因為你在安裝 npm 套件時,指令是這樣...

npm i cron

所以才會有一點混亂,但其實這兩個東西是不一樣的,只是概念上有點類似而已。

那這兩者有什麼明顯差異嗎?剛才有提到概念以及時間的格式非常雷同,但是大多在 Linux 上的 Cron 比較常用來執行 Shell Script,所以與 Node.js 的 Cron 相比會比較難以擴充成更複雜的邏輯。

Note
Shell Script 是一個純文字檔案,裡面可以寫一些指令,例如 lscdmkdir 等等,而這些指令都是可以在終端機上執行的,所以你可以把 Shell Script 想像成一個可以在終端機上執行的檔案。

簡而總之,如果你想要在 Node.js 上執行自己所撰寫的 JavaScript 程式碼,那就使用 node-cron,如果你想要在 Linux 上執行 Shell Script,那就使用 Linux 的 Cron。

那麼這一篇也差不多了,我們就先到這邊結束,我們下一篇見囉~


上一篇
Day18 - 初識 Crontab
下一篇
Day20 - Discord Bot?
系列文
《Node.js 不負責系列:把前端人員當作後端來用,就算是前端也能嘗試寫的後端~原來 Node.js 可以做這麼多事~》31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言