今天寫第二步「進入選課頁面」、第三步「至加選頁面」及第四步「查詢你想要的課程」。
這個函式涵蓋了第二步及第三步,因為不想分太多函式。
async function enter() {
// 進入選課系統
if (page.url().includes("IndexCtrl")) {
await page.click("#button-1017");
await page.waitForNavigation({ timeout: 10000 });
if (!page.url().includes("EnrollCtrl")) throw new Error("Enter Failed");
}
// 等待選課子頁面出現
await page.waitForSelector("#stfseldListDo");
// 設定自動登出時間為 6 小時
await page.evaluate(() => {
window.countSecond = 6 * 60 * 60;
});
// 取得選課子頁面,點擊加選按鈕進入加選畫面
const child_page = await (await page.$("#stfseldListDo")).contentFrame();
await child_page.waitForSelector("#add");
await child_page.click("#add");
await child_page.waitForTimeout(500);
console.log("Entered System");
}
與登入時一樣,我們用網址來判定狀態並決定是否要執行。
在這裡有個特別的地方:window.countSecond = 6 * 60 * 60
為什麼要這樣?因為系統會自動登出,但因為太相信使用者,所以不知道為什麼這個機制做在前端而非後端用讓 session 失效的方法。
接下來,因為網站是用 iframe 作系統分頁的東西,所以必須用 .contentFrame()
取得 iframe 中的東西。
這也是為什麼不該用瀏覽器的重新整理的原因
那個 #add
就是進入加選頁面最後一步的按鈕啦!
query
會先用系統找到課程的資料,對於下一步的操作至關重要。
async function query() {
// 取得子頁面
await page.waitForSelector("#stfseldListDo");
const child_page = await (await page.$("#stfseldListDo")).contentFrame();
const result = [];
// 等待所有操作的元素都出現
await Promise.all([child_page.waitForSelector("#serialNo-inputEl"), child_page.waitForSelector("#button-1059")]);
// 遍歷所有設定裡寫的課程代碼
for (const course of config.courses) {
debug(`Searching ${course}`);
// 輸入代碼,記得先清空,要不然第二個會疊上去
await child_page.$eval("#serialNo-inputEl", (elm) => (elm.value = ""));
await child_page.type("#serialNo-inputEl", course.toString().padStart(4, "0"));
await child_page.waitForTimeout(300);
// 案查詢按鈕
await child_page.click("#button-1059");
// 等待結果
await child_page.waitForSelector("#gridview-1113-body > tr");
await child_page.waitForTimeout(300);
// 從列表中拿資料,因為 id 唯一,所以應該只會有一項資料
const table = await child_page.$("#gridview-1113-body");
const data = await table.evaluate((table) => {
const data = [...table.children[0].children].map((elm) => elm.innerText);
table.innerHTML = "";
return data;
});
// 整理資料,畢竟拿到的會是一個陣列
result.push({
num: data[4].trim(),
name: data[5].trim(),
instructor: data[7].trim(),
time: data[8].trim(),
code: data[11].trim(),
class: data[19].trim(),
});
}
debug("Courses: ", result);
return result;
}
其實就是完全擬人的步驟,畢竟是自動化嘛!
明天應該會比較東西啦!
應該吧?
以 9/29 20:00 ~ 9/30 20:00 文章觀看數增加值排名
+284
讓程式碼化為 API Doc
+273
終章 - 資安碎碎念與心得
+224
[Day 28] 資料產品開發實務 - 非機器學習模型
+205
Day 1 無限手套 AWS 版:掌控一切的 5 + 1 雲端必學主題
+197
[Day30] 完結灑花❀ 看完賽心得順便用Python畫 3D 漸層花朵!
+179
[重構倒數第30天] - 使用 Vue3 Composition API 重構 JS 選單
+178
[DAY-16] 找出你珍視的機會
+151
EP 22
+147
Proxmox VE 安裝容器:Ubuntu 20.04
+144
Day 3 雲端四大平台比較:AWS . GCP . Azure . Alibaba
有人完賽了!恭喜!!
現在也完成一半了,希望撐得下去。