建立 cloud functions
抓取電影時刻表,讓 cloud scheduler
可以定期觸發,並將今日放映的電影資訊顯示在 Slack
上。
將先前寫的抓取電影時刻的程式碼使用 @google-cloud/functions-framework
包成 cloud functions 形式,選擇 HTTP Trigger,並勾選 「需要驗證」。
index.js 內容如下:
'use strict';
const functions = require("@google-cloud/functions-framework");
const axios = require("axios");
const cheerio = require("cheerio");
const url = "https://meet.eslite.com/tw/tc/gallery/movieschedule/201803020001"; // 目標網址
// getTimetable 為 entry point
functions.http("getTimetable", async(req, res) => {
let externalRes = await axios.get(url);
let $ = cheerio.load(externalRes.data);
let list = [];
$(".film_list .box").each(function(i, elem) {
let name = $(this).find(".intro .left > p").text();
let timetable = [];
$(this).find(".time-swiper .swiper-slide").each(function (j, slide) {
let date = $(slide).find("p").text();
let time = [] ;
$(slide).find("ul li").each(function(k, text) {
time.push($(text).text());
})
timetable.push({date, time});
})
list.push({name, timetable});
});
res.status(200).send(list);
})
Cloud Scheduler
可以用來呼叫需驗證的 HTTP targets
,例如:Cloud Functions
or Cloud Run
。Function URL
即為要執行的目標(HTTP targets)。
注意事項:
Cloud Scheduler
在同一個專案中Cloud Scheduler
預設的服務帳戶Google Cloud
中的目標對象,可透過 IAM (Identity and Access Management)
管理服務帳戶
*補充:服務帳戶 Service account?
利用服務帳戶來呼叫需驗證的 API,服務帳戶可作為 IAM 主體/使用者,管理服務帳戶對Google Cloud
資源的存取權限。
服務帳戶類似於在Google Cloud
建立一個虛擬使用者。
進入IAM 與管理 - 服務帳戶,點擊建立服務帳戶
進入 「Cloud Scheduler」 點擊建立工作
0 8 * * *
、選擇台北時區
*gcloud 操作可參考 Google Cloud 官方文件 - Use authentication with HTTP targets
假設需要將 Functions 執行的結果送到 Slack 通知,可依 Sending messages using Incoming Webhooks 說明操作。
透過 Slack 的 Block Kit Builder 客製訊息的樣式,或是直接選擇現成的 Template 進行調整
使用 try...catch 處理錯誤,index.js 建立 notifySlack 函式傳送 slack 訊息。
// index.js
// 略
const notifySlack = async (payload) => {
await axios.post(slackUrl, payload)
}
functions.http('getTimetable', async(req, res) => {
try {
// 略
notifySlack('成功取得電影時刻');
} catch (e) {
notifySlack('無法取得電影時刻');
}
})
使用 Slack 的 Block Kit Builder 將欲顯示的電影資訊做樣式排版,製作 Slack 訊息的樣板,如下圖:
完整 Cloud Functions 內容
'use strict';
const functions = require('@google-cloud/functions-framework');
const axios = require("axios");
const cheerio = require("cheerio");
const url = "https://meet.eslite.com/tw/tc/gallery/movieschedule/201803020001";
const slackUrl = "https://hooks.slack.com/services/xxxxxxxxxxxxxx";
const movieResultTemplate = require("./slack-movie-card");
const notifySlack = async (payload) => {
await axios.post(slackUrl, payload)
}
functions.http('getTimetable', async(req, res) => {
try {
let externalRes = await axios.get(url);
let $ = cheerio.load(externalRes.data);
let list = [];
$(".film_list .box").each(function(i, elem) {
// 電影名稱
let name = $(this).find(".intro .left > p").text();
// 電影介紹連結
let link = `https://meet.eslite.com${$(this).find(".intro .right .btn-detail").attr("href")}`;
// 電影縮圖連結
let thumbUrl = `https://meet.eslite.com${$(this).find(".img img").attr("src")}`;
// 電影縮圖文字
let thumbAlt = $(this).find(".img img").attr("alt");
// 電影資訊
let infos = [];
// 時刻表
let timetable = [];
// 取得電影資訊 (級別、片長...)
$(this).find(".intro .right > ul > li").each(function (index, li) {
let info = $(li).text();
infos.push(info);
});
// 取得電影每日時刻
$(this).find(".time-swiper .swiper-slide").each(function (j, slide) {
let date = $(slide).find("p").text();
let time = [];
$(slide).find("ul li").each(function(k, text) {
time.push($(text).text());
})
timetable.push({date, time});
})
list.push({name, link, thumbUrl, thumbAlt, infos, timetable})
});
// ---- 轉換成 slack template 格式 ----
let movieResult = {...movieResultTemplate.result},
cardResult = [],
today = `${new Date().getMonth() + 1}/${new Date().getDate()}`;
// 遍歷每一部電影資訊
list.forEach((item) => {
const regex = /\d+\/\d+/;
// 判斷第一個放映日期是否為今日
let movieDate = item.timetable[0].date.match(regex)[0];
if (movieDate !== today) return;
let time = item.timetable[0].time.join(' | ');
let card = JSON.parse(JSON.stringify(movieResultTemplate.card));
let infos = item.infos.join('\n');
card[0].text.text = `*<${item.link}|${item.name}>*\n${infos}\n時刻: ${time}`;
card[0].accessory.image_url = item.thumbUrl;
card[0].accessory.alt_text = item.thumbAlt;
cardResult.push(...card);
})
movieResult.blocks[0].text.text = `${today} 今日放映 :movie_camera:`
movieResult.blocks.push(...cardResult);
notifySlack((movieResult));
res.status(200).send(list);
} catch (e) {
console.log(e);
notifySlack({text:`:bangbang: 無法取得電影時刻`, emoji: true});
}
})
每日 8:00 排程會執行 Cloud Functions,到電影時刻表網頁抓取資訊,將相關資訊整理成 Slack 訊息格式,透過 Slack Webhook 發送訊息到 Channel 中。
*Slack 訊息
*排程執行狀態
Use authentication with HTTP targets
Using-scheduler-invoke-private-functions-OIDC
Sending messages using Incoming Webhooks