つよつよエンジニアへの道

寝て食べてまた寝ます

スクレイピングとLINEでバス時刻表BOTをつくる

田舎に住んでいると,交通機関が利用できる時間が限られてしまいます.電車は一時間に一本...
そう,すべては電車のために行動していると言っても過言ではないです!

そこで,今回はスクレイピングによりバス会社のサイトから時刻表を取得して,LINEから情報を得られるようなBOTちゃん作りです.

目次

全体構成

最初に全体の構成を説明します.
f:id:ryutyan0424:20191208040010p:plain
バス会社のWebからGASを利用してスクレイピングを行い,スプレッドシートにデータを集めます.(第一過程)
次に,スマホからのメッセージによって最適な時刻表を送るLINE BOTの制作をします.(第二過程)

制作

スクレイピング(第一過程)

スクレイピングがなんじゃらほいって人はこれ見て出直してきてね.
スクレイピング (scraping)とは

スクレイピングは色々な方法がありますが,今回は比較的簡単なGoogle スプレッドシートを利用して行います. qiita.com こちらを参考に行いました.

XpathLを見つける作業結構大変です.つまづいたポイントとして,コピーしたXpathが正しいとは限りません.

//*[@id="trendinggames"]/tbody/tr[1]/td[4]

仮にこのようなXpathだとしても,[1]が[2]であったり,tr[1]が不要だったりと調整が必要です.

f:id:ryutyan0424:20191208042437p:plain 取得が完了したら,スプレッドシートに綺麗に並べ替えます.

LINE BOT制作(第二過程)

今回作ったコードがこちら

//LINE developersのメッセージ送受信設定に記載のアクセストークン
var ACCESS_TOKEN = '<アクセストークン>';

function favo(userId,favo_num) {
    var spreadsheet_f = SpreadsheetApp.getActiveSpreadsheet();
    var sheet_f = spreadsheet_f.getSheetByName('user');
    var userlist = sheet_f.getRange(2,2, sheet_f.getLastRow(),1).getValues();
    var sent_message = "User Not Found";
    for(var i = 0; i < userlist.length; i++){
        var ID = userlist[i];
        if(ID == userId){
            var favo_bus = sheet_f.getRange(i+2,favo_num+2).getDisplayValue();
            sent_message = bus_time(favo_bus + '');
            break;
        }
    }
    return sent_message;
}

function doPost(e) {

    //WebHookで受信した応答用Token
    var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;

    //ユーザーのメッセージを取得
    var userMessage = JSON.parse(e.postData.contents).events[0].message.text;

    //応答メッセージ用のAPI URL
    var url = 'https://api.line.me/v2/bot/message/reply';

    //ユーザーID取得
    var json = JSON.parse(e.postData.contents);
    var userId = json.events[0].source.userId;

    //メッセージ判別
    var sent_message
    switch(userMessage){
        //いわき駅前:1,高専前:2,ヨークタウン谷川瀬:3,小名浜車庫:4,神白:5,鹿島SC:6,飯野2丁目:7
        case"12":
        case"21":
        case"13":
        case"31":
        case"24":
        case"42":
        case"25":
        case"52":
        case"26":
        case"62":
        case"27":
        case"72":
            sent_message = bus_time(userMessage);//バス時刻表関数
            break;
        case"favo1":
        case"favo2":
        case"favo3":
        case"favo4":
            var favo_num = {"favo1":1, "favo2":2, "favo3":3, "favo4":4};
            sent_message = favo(userId,favo_num[userMessage]);
            break;
        case"使い方":
            sent_message =
            "◯✕で2桁の数字を入力してください.\n"+
            "◯:出発地  ✕:到着地\n"+
            "1:いわき駅\n"+
            "2:高専前\n"+
            "3:ヨークタウン谷川瀬\n"+
            "4:小名浜車庫\n"+
            "5:神白\n"+
            "6:鹿島SC\n"+
            "5:飯野2丁目\n";
            break;
        case"UID":
            sent_message = userId;
            break;
        default:
            sent_message = "有効なメッセージではありません.";
            break;
    }
    //送信
    UrlFetchApp.fetch(url, {
        'headers': {
            'Content-Type': 'application/json; charset=UTF-8',
            'Authorization': 'Bearer ' + ACCESS_TOKEN,
        },
        'method': 'post',
        'payload': JSON.stringify({
            'replyToken': replyToken,
            'messages': [{
                'type': 'text',
                'text': sent_message,
            }],
        }),
    });
    return ContentService.createTextOutput(JSON.stringify({ 'content': 'post ok' })).setMimeType(ContentService.MimeType.JSON);
}
function bus_time(userMessage) {
    // 時間取得
    var date = new Date();
    var date_weekday = date.getDate(); 
    var time_hour = date.getHours();
    var time_min = date.getMinutes();
    var time_now = time_hour * 100 + time_min; //現在時刻4桁

    //スプレットシート取得
    var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = spreadsheet.getSheetByName('main');
    var timelist_start, timelist_goal;
    var start_name, goal_name;

    var cell_num = {"12":2, "21":4, "13":6, "31":8, "24":10, "42":12, "25":14, "52":16,"26":18,"62":20,"27":22,"72":24}

    timelist_start = sheet.getRange(2,cell_num[userMessage], sheet.getLastRow(),1).getValues();
    timelist_goal = sheet.getRange(2,cell_num[userMessage]+1, sheet.getLastRow(),1).getValues();
    start_name = sheet.getRange(1,cell_num[userMessage]).getDisplayValue();
    goal_name = sheet.getRange(1,cell_num[userMessage]+1).getDisplayValue();

    //時間データを配列へ
    var start_time = [];
    var goal_time = [];
    var time_s; //計算用
    var time_g;
    for (var i in timelist_start) {
        time_s = new Date(timelist_start[i]); //時刻表
        time_g = new Date(timelist_goal[i]);
        time_sa = new Date(timelist_start[i-1]); //ひとつ前の時刻表取得
        time_ga = new Date(timelist_goal[i-1]);
        if (time_now <= time_s.getHours() * 100 + time_s.getMinutes()) {
            start_time.push(time_sa.getHours() + ':' + ("0" + time_sa.getMinutes()).slice(-2));
            goal_time.push(time_ga.getHours() + ':' + ("0" + time_ga.getMinutes()).slice(-2));
            if (start_time.length == 5) break;
        }
    }

    //メッセージ作成    
    var sent_message;
    sent_message =
    start_name + "   " + goal_name + "\n" + "---------------------" + "\n" +
    start_time[0] + " → " + goal_time[0] + "\n" + "---------------------" + "\n" +
    start_time[1] + " → " + goal_time[1] + "\n" + "---------------------" + "\n" +
    start_time[2] + " → " + goal_time[2] + "\n" + "---------------------" + "\n" +
    start_time[3] + " → " + goal_time[3] + "\n" + "---------------------" + "\n" +
    start_time[4] + " → " + goal_time[4] + "\n" + "---------------------" + "\n" +
    "現在時刻:" + time_hour + ":" + time_min;
  
  return sent_message;
}

スプレッドシートのGASで動きます.
((この記事はまだ編集途中です.続きます.))