Skip to content

Instantly share code, notes, and snippets.

@stepney141
Last active September 26, 2023 07:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stepney141/329ef8b5be55b2d355831109f7eced48 to your computer and use it in GitHub Desktop.
Save stepney141/329ef8b5be55b2d355831109f7eced48 to your computer and use it in GitHub Desktop.
履修ポチポチ楽しい!
LOYOLA_ID=A1234567
LOYOLA_PASSWORD=password
const puppeteer = require('puppeteer');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, "./.env") }); //認証情報は別ファイルから読み込む
const setting = {
loyola_id: process.env.LOYOLA_ID, //学籍番号
loyola_password: process.env.LOYOLA_PASSWORD, //パスワード
loyola_uri: "https://scs.cl.sophia.ac.jp/campusweb/campusportal.do",
};
const loyola_xpath = {
login_username: '//input[@name="userName"]',
login_password: '//input[@name="password"]',
login_button: '//input[@value="ログイン"]',
time_extension_clock: '//*[@id="portaltimer"]', //時間延長ボタン
button_of_curriculum_menu: '//*[@id="tab-rs"]', //ホーム画面ヘッダーのカリキュラム/履修登録メニューのアイコン
};
// ref: https://qiita.com/albno273/items/c2d48fdcbf3a9a3434db
// example: await sleep(randomWait(1000, 0.5, 1.1)); 1000ms x0.5 ~ x1.1 の間でランダムにアクセスの間隔を空ける
const sleep = async (time) => new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, time); });
const randomWait = (baseWaitSeconds, min, max) => baseWaitSeconds * (Math.random() * (max - min) + min);
const login = async (page) => {
try {
await page.goto(setting.loyola_uri, { //LOYOLAトップページに飛ぶ
waitUntil: 'domcontentloaded',
});
const userNameInputHandle = await page.$x(loyola_xpath.login_username);
const passwordInputHandle = await page.$x(loyola_xpath.login_password);
const loginButtonHandle = await page.$x(loyola_xpath.login_button);
await userNameInputHandle[0].type(setting.loyola_id); //学籍番号入力
await passwordInputHandle[0].type(setting.loyola_password); //パスワード入力
await Promise.all([
page.waitForNavigation({ waitUntil: ["domcontentloaded", "networkidle0"] }),
loginButtonHandle[0].click() //ログインボタンを押す
]);
console.log('Loyolaログイン完了');
} catch (e) {
console.log(e);
return false;
}
return page;
};
const access_entry_form = async (page) => {
try {
const curriculum_menu_handle = await page.$x(loyola_xpath.button_of_curriculum_menu);
await Promise.all([
page.waitForXPath(loyola_xpath.button_of_curriculum_menu),
// page.waitForTimeout(3000),
curriculum_menu_handle[0].click() //「カリキュラム」のメニューを開く
]);
await page.waitForTimeout(2000);
const header_handle = await page.$x('//*[@id="tabmenu-li1"]/span');
await Promise.all([
page.waitForXPath('//*[@id="tabmenu-sub-ul1"]/li[1]/span'),
// page.waitForTimeout(3000),
header_handle[0].click() //ヘッダの「履修登録」を開く
]);
await page.waitForTimeout(2000);
const link_to_entry_page_handle = await page.$x('//*[@id="tabmenu-sub-ul1"]/li[1]/span');
await Promise.all([
page.waitForFrame(async (frame) => frame.url().includes('scs.cl.sophia.ac.jp')),
link_to_entry_page_handle[0].click() //ヘッダから履修登録エントリーページを開く
]);
await page.waitForTimeout(2000);
console.log('抽選科目エントリーページへのアクセス:成功');
} catch (e) {
console.log(e);
return false;
}
return page;
};
const continue_clicking = async (page) => {
try {
const [, iframe] = await page.frames(); //現在のページが[0]に入っている(n>0なるn個目のiframeは[n]に入る)
const expand_loggedin_time = async () => {
const eh = await page.$x(loyola_xpath.time_extension_clock);
await eh[0].click();
};
page.on("popup", expand_loggedin_time);
const mokusan_eh = await iframe.$x('/html/body/table[2]/tbody/tr[2]/td/table/tbody/tr[6]/td[4]');
await Promise.all([
iframe.waitForNavigation({ waitUntil: ["domcontentloaded", "networkidle0"] }),
mokusan_eh[0].click() //時間割表の水曜4限をクリック
]);
const input_eh = await iframe.$x('//*[@id="jikanwariCode"]');
await input_eh[0].type('LAW62200', { delay: 120 }); //科目コード入力
let cnt = 0;
for (; ;) {
const submit01_eh = await iframe.$x('//*[@id="rishuReferUpdateForm"]/p[2]/input[1]');
await Promise.all([
iframe.waitForNavigation({ waitUntil: ["domcontentloaded", "networkidle0"] }),
submit01_eh[0].click() //「登録」をクリック
]);
await sleep(randomWait(2000, 0.5, 1.1));
const submit02_eh = await iframe.$x('/html/body/p[3]/input[1]');
await Promise.all([
iframe.waitForNavigation({ waitUntil: ["domcontentloaded", "networkidle0"] }),
submit02_eh[0].click() //「OK」をクリック
]);
await sleep(randomWait(2000, 0.5, 1.1));
const error_eh = await iframe.$x('//span[@class="error"]');
if (await error_eh.length === 0) {
console.log(`登録成功: ${new Date().toLocaleString()}`);
break;
} else {
cnt++;
console.log(`登録失敗: ${cnt}回目`);
}
}
} catch (e) {
console.log(e);
}
return page;
};
(async () => {
const browser = await puppeteer.launch({
defaultViewport: { width: 1000, height: 1000 },
// args: [
// '--disable-gpu',
// '--disable-dev-shm-usage',
// '--disable-setuid-sandbox',
// '--no-first-run',
// '--no-sandbox',
// '--no-zygote',
// '--single-process'
// ],
// headless: true,
headless: false,
// devtools: true,
timeout: 2 * 60 * 1000,
slowMo: 25
});
try {
const page = await browser.newPage();
await page.evaluateOnNewDocument(() => { //webdriver.navigatorを消して自動操縦であることを隠す
Object.defineProperty(navigator, 'webdriver', ()=>{});
delete navigator.__proto__.webdriver;
});
await continue_clicking(await access_entry_form(await login(page)));
} catch (e) {
console.log(e);
} finally {
await browser.close();
}
})();
{
"dependencies": {
"dotenv": "^16.3.1",
"puppeteer": "^21.3.4"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment