立命館学食ガチャを作った話(GAS+GitHub Pages)
こんにちは、M1のYukimuraです。
最近やることが多すぎて一周回って暇になっています。そういうときってありますよね。定期テストの前に無駄にマンガ読むのに集中できたり、ラスボス直前でゲームやる気無くなったり。
そんな折、研究室でおしゃべりに興じていたら、B4の菅原くんから「こんなページがあるんですけど、生協メニューでガチャ作ったら面白いと思うんですよね」と言われたので菅原くんが取り掛かる前に完成させてやろうと思って、1日で作ってみました。ネタを盗んでごめんなさい。
技術的には、GASとGithub Pagesだけで作ったので誰でもかんたんに再現できると思います。多分。
元ネタとか
そもそもこの「レストランメニューでガチャ」っていうのは元ネタがあって、サイゼリヤ1000円ガチャを参考にしました。 参考にしたというかデザインなどは完全にパク…リスペクトです。
本家は1000円まででサイゼリヤのメニューを選びますが、今回は学食で500~550円のメニューを選びます。
なぜこの価格帯かというと、立命館にはミールという学食の定期契約みたいなやつがあって、それの1日の使用上限が、多くの人が500円だからです。 訂正:550円でした。僕が学部生のときは500円だったと思ったのですが….
何より、学食一食で1000円分も食べたら食べ過ぎです。体育会でも無い限り。
ミールの話は京大生協の食堂メニューの最適解を探るでも触れられていて、全休の日も嵐の日も大学に行き学食を食べないと元が取れないし、弊学の昼の学食は春夏秋冬いつでも混んでいるということもあり、非常に辛いです。(なので僕は3回生に上がるときに解約しました。コロナ禍になったのでちょうどよかったです)
そんなわけで、ミールの消費で困っている学生の一助になればという思いで作りました。そういうことにしておいてください。
できたもの
完成物はこちらです。(画像をタップするとページに移動します)
最初は500円ガチャだけ作っていたのですが、万万が一本当に今日の学食を検討する目的で使う人がもしもいたら悪いな、と思い、主食確定ガチャも作りました。主食確定ガチャでは、「ライス」「麺」「丼・カレー」「定食」のいずれかが必ず排出されます。
やればわかるのですが、主食系って結構レアなので、この機能は作ってよかったです。500円ガチャの方は上の画像のように、たまに副菜とデザートだけで埋まります。最悪。
あそびかた
主食確定ガチャも同じです。ツイートするボタンがあるので、ツイートして頂けると「あっ遊んでもらえてるな」と作者が嬉しくなります。
つくりかた
スクレイピング部
先述の通り、GASでスクレイピングしました。
UrlFetchでリクエストをして、返ってきたレスポンスに対してParserライブラリを利用して必要部分だけ切り出しました。
生協のページが、開いた時点で全メニューが表示されているわけではないのでseleniumとかで操作しないと無理かなと思ったのですが、どうもクリックしたときにリクエストを送っているURLが特定できたので、GASだけで済みました。
function getweeklymenu_union() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.deleteRows(2,100)
let plist=["a","b","c","d","e"]
let catlist=["主菜","副菜","麺類","丼・カレー","デザート"]
for(let p=0;p<5;p++){
let response = UrlFetchApp.fetch("https://west2-univ.jp/sp/menu_load.php?t=650311&a=on_"+plist[p]);
let text = response.getContentText("utf-8");
let url_list=Parser.data(text).from('<a href="').to('">').iterate();
let title_list=Parser.data(text).from("<h3>").to('<').iterate();
for(let i=0;i<title_list.length;i++){
let response2 = UrlFetchApp.fetch("https://west2-univ.jp/sp/"+url_list[i]);
let text2 = response2.getContentText("utf-8");
//let price=Parser.data(text2).from('<span class="price"><strong>').to('</strong>円').build();
let vals=Parser.data(text2).from('<span class="price">').to('</span>').iterate();
let li=[];
li.push(catlist[p]);
li.push(title_list[i]);
//li.push(price);
for(let j=0;j<vals.length;j++){
li.push(vals[j].replace(/[^0-9.]/gi,''));
}
let three_gs=Parser.data(text2).from('</span></span>').to('</li>').build();
let three_li=Parser.data(three_gs).from('<span>').to('<span class="en">').iterate();
for(let j=0;j<3;j++){
li.push(three_li[j].replace(/[^0-9.]/gi,''));
}
if(li[2]>1000){
li[2]=li[2].slice(0,3);
}
li.push("https://west2-univ.jp/sp/"+url_list[i]);
sheet.appendRow(li)
}
}
}
こんな感じに気合でかけます。
あとは、毎週変わる生協メニューに対応するために、GASのトリガー設定を月曜の早朝とかに設定してやれば、完璧です。
フロント
全部生JSとHTMLで書きました。公開はgithub pagesでやっています。
リポジトリはここなので、興味のある人はどうぞ。PR歓迎です。
こんな感じで500円ガチャは回っています。ただしかしこれ難点があって、序盤で主食系を引かないと永遠に小鉢だけ引き続けるという問題が発生します。改善できるぞという人はPRください。
おわりに
GASでスクレイピングができること・ガチャをJSで回せることで、GASとGithub Pagesだけで学食500円ガチャができました。
早速後輩が手を加えてプルリクを投げてくれているので、今後僕の書いたコードはどんどん減って行く気がしますが、「優しい終身の独裁者」的な身分を楽しんでみたいなと思います。
追記:記事をチンタラ書いていたせいで、菅原くんが900円ガチャを追加してくれました。ぜひ色々遊んでみてください。
(文責:2022年度M1 木村悠生)
参考にした記事
@iroha_nano145:京大生協の食堂メニューの最適解を探る:Qiita
@marusho_summers:サイゼリヤ1000円ガチャをつくってみた(Heroku + Flask + LINEbot):Qiita