はじめに
本稿は下記の記事からパクリ着想を得て記載しています。
GASのスクレイピングとSlackボット作成をしたかったので、作ってみました。
できること
食べログのランキングからランダムにN個を取り出してSlackに投稿する。
完成したコード
//required library
//SlackApp M3W5Ut3Q39AaIwLquryEPMwV62A3znfOO
//Parser M1lugvAXKKtUxn_vdAG9JZleS6DrsjUUV
//Fisher–Yates shuffle(配列をシャッフルする関数)
function shuffle(array) {
var n = array.length, t, i;
while (n) {
i = Math.floor(Math.random() * n--);
t = array[n];
array[n] = array[i];
array[i] = t;
}
return array;
}
//食べログをスクレイピングしてSlackに投稿する。
function postMessage() {
const prop = PropertiesService.getScriptProperties().getProperties();
const slackApp = SlackApp.create(prop.token); //Script Propertiesに設定したトークンIDを取得
const slackChannelId = prop.SlackChannel; //Script Propertiesに設定したチャンネルIDを取得
const message = [
'今日のおすすめランチです。',
'ランチはここでどうでしょう?',
'ランチはここで食べてください。'
];
//食べログのURL 下記形式のURLであればそのまま使えそう。https://tabelog.com/*/rstLst/*
//サンプルは東京駅のランチランキング
const fetchUrl = 'https://tabelog.com/tokyo/A1302/A130201/R6586/rstLst/lunch/';
const html = UrlFetchApp.fetch(fetchUrl).getContentText();
//ランキングのリスト部分をパースする。
const doc = Parser.data(html)
.from('<ul class="js-rstlist-info rstlist-info"')
.to('<li class="list-rst__rdimg">')
.build()
//名前、価格etcを配列で格納。
//現状は名前しか使っていない。できれば結合して投稿したい。
const name = Parser.data(doc)
.from('<div class="list-rst__rst-name">')
.to('<span class="list-rst__area-genre cpy-area-genre">')
.iterate()
const price = Parser.data(doc)
.from('<span class="c-rating__val list-rst__budget-val cpy-lunch-budget-val">')
.to('</span>')
.iterate()
const rating = Parser.data(doc)
.from('<span class="c-rating__val c-rating__val--strong list-rst__rating-val">')
.to('</span>')
.iterate()
const url = Parser.data(doc)
.from('<a class="list-rst__rst-name-target cpy-rst-name" target="_blank" href="')
.to('">')
.iterate()
//メッセージとランキングをシャッフル
var randomMsg = shuffle(message);
var randomName = shuffle(name);
//3つを取り出してSlackに投稿
slackApp.postMessage(slackChannelId,randomMsg[0], {username : "ランチロシアンルーレット"});
for (var j=0; j<3 ;j++){
slackApp.postMessage(slackChannelId,j+1+'. '+randomName[j].replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,''), {username : "ランチロシアンルーレット"});
}
}
コードの説明
//required library
//SlackApp M3W5Ut3Q39AaIwLquryEPMwV62A3znfOO
//Parser M1lugvAXKKtUxn_vdAG9JZleS6DrsjUUV
事前準備として、必要なライブラリをインポートします。
//Fisher–Yates shuffle(配列をシャッフルする関数)
function shuffle(array) {
var n = array.length, t, i;
while (n) {
i = Math.floor(Math.random() * n--);
t = array[n];
array[n] = array[i];
array[i] = t;
}
return array;
}
お店をどうやってランダムに取り出すか迷いましたが、配列をシャッフルして先頭から取り出すことにしました。
このため、シャッフルするための関数を用意しておきます。
//食べログをスクレイピングしてSlackに投稿する。
function postMessage() {
const prop = PropertiesService.getScriptProperties().getProperties();
const slackApp = SlackApp.create(prop.token); //Script Propertiesに設定したトークンIDを取得
const slackChannelId = prop.SlackChannel; //Script Propertiesに設定したチャンネルIDを取得
const message = [
'今日のおすすめランチです。',
'ランチはここでどうでしょう?',
'ランチはここで食べてください。'
];
Slackの投稿に必要なトークンとチャンネルのIDは、コード内に含めずに、スクリプトのプロパティに定義しておき、PropertiesService.getScriptProperties().getProperties()
で取り出します。
//食べログのURL 右記形式のURLであればそのまま使えそう。 https://tabelog.com/*/rstLst/*
//サンプルは東京駅のランチランキング
const fetchUrl = 'https://tabelog.com/tokyo/A1302/A130201/R6586/rstLst/lunch/';
食べログのURLを指定します。
「https://tabelog.com/*/rstLst/*」 という形式のURLであれば、今回のコードがそのまま使えるようです。
const html = UrlFetchApp.fetch(fetchUrl).getContentText();
//ランキングのリスト部分をパースする。
const doc = Parser.data(html)
.from('<ul class="js-rstlist-info rstlist-info"')
.to('<li class="list-rst__rdimg">')
.build()
//名前、価格etcを配列で格納。
//現状は名前しか使っていない。できれば結合して投稿したい。
const name = Parser.data(doc)
.from('<div class="list-rst__rst-name">')
.to('<span class="list-rst__area-genre cpy-area-genre">')
.iterate()
const price = Parser.data(doc)
.from('<span class="c-rating__val list-rst__budget-val cpy-lunch-budget-val">')
.to('</span>')
.iterate()
const rating = Parser.data(doc)
.from('<span class="c-rating__val c-rating__val--strong list-rst__rating-val">')
.to('</span>')
.iterate()
const url = Parser.data(doc)
.from('<a class="list-rst__rst-name-target cpy-rst-name" target="_blank" href="')
.to('">')
.iterate()
ライブラリParserを利用して、ページの情報を取得します。
必要な情報が含まれる箇所のHTMLタグをfrom
/to
で指定して、build()
で文字列として取得します。名称や星の数はliterate()
で配列として格納します。
なお、このコードでは、名前しか使用していません。
本当は星の数やURLも一緒に投稿したかったのですが、うまい紐づけかたが思い浮かびませんでした。(コメント貰えると嬉しいです)
//メッセージとランキングをシャッフル
var randomMsg = shuffle(message);
var randomName = shuffle(name);
いつも同じメッセージだとつまらないので、冒頭で定義したshuffle
ファンクションを利用して、シャッフルしたメッセージを取り出します。
また、お店の名前はこのままだとランキングの順位で並んでいるだけなので、こちらもシャッフルします。
//3つを取り出してSlackに投稿
slackApp.postMessage(slackChannelId,randomMsg[0], {username : "ランチロシアンルーレット"});
for (var j=0; j<3 ;j++){
slackApp.postMessage(slackChannelId,j+1+'. '+randomName[j].replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,''), {username : "ランチロシアンルーレット"});
}
}
ライブラリSlackAppを利用してSlackに投稿します。
先ほどシャッフルした配列randomName
から順番に3つを取り出します。
配列name
(とそれをシャッフルしたrandomName
)には<a>タグが含まれてしまうので、replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'')
でタグを削除します。
JavaScriptでHTMLタグを削除する正規表現 - Qiita
できあがり
あとがき
本文中にも書きましたが、できれば店の名前とURLや星の数を紐づけて投稿したいなと思っています。