1. 0505Keitan

    Posted

    0505Keitan
Changes in title
+ディズニーリゾートの待ち時間をGASを使ってLINEで見れるようにした話
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,108 @@
+この記事は[N高等学校 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/n-highschool)の最終日です。
+
+こんにちは。N高等学校2年の0505Keitanと申します。
+クリスマスなので今回はディズニーの待ち時間をLINEで見れるようにしたことでも書こうかと思います。←!?
+
+# 作ったもの
+以下の画像のように待ち時間を垂れ流します。
+今のうちに言っておきますが、**完全に個人用途**です。公開する気など全くありません。というかしたくない。
+<img width="1374" alt="LINE_capture_597049992.470206 2.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179129/2e912096-8fc2-137c-5019-54accdb65b03.jpeg">
+
+データ取得先なんですけど、ディズニーは待ち時間APIを出していない(当たり前)なので、[tokyodisneyresort.info](https://tokyodisneyresort.info)の情報を使わせていただくことにしました。このサイトに10分に一回(7分間隔)アクセスし、スクレイピングして更新、表示させます。
+
+念のためサーバーに過負荷を掛けさせないため、要求ごとに取得させるのではなく、取得したデータを一度スプレッドシートに保存し、メッセージごとに呼び出しています。
+
+[詳しく見る](https://scrapbox.io/0505Keitan/TokyoDisneyResort_Unofficial_LINE_Account)
+
+# 使用技術
+- Google Apps Script
+- Google Spreadsheet
+- Parser
+- Moment
+
+実行フローとしては
+① [tokyodisneyresort.info](https://tokyodisneyresort.info)にアクセス
+② 取得データを整形して、Googleスプレッドシートに保存
+③ LINEからWebhookでリクエストが飛んでくる
+④ スプレッドシートに情報を取りに行く
+⑤ データをGASに渡す
+⑥ GASからLINEに送信する
+という順番です。
+
+![qiita.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179129/eb22c48b-f5ec-06ab-f7ac-e7b5308a93cd.jpeg)
+
+# コード
+結構長くて処理複雑なので主要部分だけ書きます。
+
+#### ランドとシーのスタンバイ時間を配列で出す。
+```js:コード.gs
+var response = UrlFetchApp.fetch('https://tokyodisneyresort.info/realtime.php?park=land&order=name', {'validateHttpsCertificates' : false});
+var response_c = UrlFetchApp.fetch('https://tokyodisneyresort.info/realtime.php?park=sea&order=name', {'validateHttpsCertificates' : false});
+var html = response.getContentText('UTF-8');
+var html_c = response_c.getContentText('UTF-8');
+
+var data = Parser.data(html).from('<div class="attr_name">').to('</div>').iterate();
+var data_c = Parser.data(html_c).from('<div class="attr_name">').to('</div>').iterate();
+var fdata = Parser.data(html).from('<div class="attr_wait">').to('</div>').iterate();
+var fdata_c = Parser.data(html_c).from('<div class="attr_wait">').to('</div>').iterate();
+
+for(i=0;i<data.length;i++){
+ data[i]=data[i].replace(/ /g,'').replace('&park=land">','').replace('</a>','').replace('<span class="new">NEW</span>','').replace('amp;','').replace(/\n/g,'').replace(/ /g,'');
+ fdata[i]=fdata[i].replace(/<span class="fp">【FP:(TICKETING|TICKETING_END|NOT_TICKETING_TODAY)】<\/span>/g,'(FP対象)').replace(/<span class="greeting_timetable">/g, '').replace(/<\/span>/g, '').replace(/ /g,'').replace(/\n/g,'').replace(/<br\/>/g,'\n').replace(/分/g,'分').replace(/ /g, '').replace(/^$/g, '情報なし');
+ data[i]='【'+data[i]+'】\n'+fdata[i]+'\n\n';
+}
+for(i=0;i<data_c.length;i++){
+ data_c[i]=data_c[i].replace(/ /g,'').replace('&park=sea">','').replace('</a>','').replace('<span class="new">NEW</span>','').replace('amp;','').replace(/\n/g,'').replace(/ /g,'');
+ fdata_c[i]=fdata_c[i].replace(/<span class="fp">【FP:(TICKETING|TICKETING_END|NOT_TICKETING_TODAY)】<\/span>/g,'(FP対象)').replace(/<span class="greeting_timetable">/g, '').replace(/<\/span>/g, '').replace(/ /g,'').replace(/\n/g,'').replace(/<br\/>/g,'\n').replace(/分/g,'分').replace(/ /g, '').replace(/^$/g, '情報なし');
+ data_c[i]='【'+data_c[i]+'】\n'+fdata_c[i]+'\n\n';
+}
+```
+
+#### LINEへ送信
+```js:コード.gs
+function reply(message, token) {
+ UrlFetchApp.fetch(line_endpoint, {
+ 'headers': {
+ 'Content-Type': 'application/json; charset=UTF-8',
+ 'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
+ },
+ 'method': 'post',
+ 'payload': JSON.stringify({
+ 'replyToken': token,
+ 'messages': [{
+ 'type': 'text',
+ 'text': message,
+ }],
+ }),
+ });
+ return;
+}
+```
+LINEでレスポンスをするには`doPost`に含まれている`replyToken`を指定しなければなりません。
+
+
+# 問題点と改善点
+#### ①22:00以降は結果が同じになる
+実行しても同じ結果なので22:00以降は各処理前に時間を取得してreturnさせた。(もっといい方法があるはず)
+
+```js:コード.gs
+if(Moment.moment().format('HH')>=22||Moment.moment().format('HH')<08){
+ sheet_standby.getRange('A1').setValue('東京ディズニーリゾートは現在閉園中です。');
+ sheet_standby.getRange('B1').setValue('東京ディズニーリゾートは現在閉園中です。');
+ sheet_standby.getRange('A2:B2').setValue('最終取得時間:'+Moment.moment().format('HH:mm'));
+ return;
+ }
+```
+
+#### ②GASでアクセスするときSSLエラーが出る
+(あまりいい方法ではないが)
+無理やりアクセスさせるために`UrlFetchApp`に`{'validateHttpsCertificates' : false}`をつけた
+
+```js:コード.gs
+UrlFetchApp.fetch('https://tokyodisneyresort.info/realtime.php?park=land&order=name', {'validateHttpsCertificates' : false});
+```
+
+# まとめ
+やっぱりGASはいいですよね(あとディズニー)!!!
+ぜひ公式API出して欲しいです。
+実装もすぐに終わるのでGASはぜひ使いましょう!!!本当に便利です。