【GAS】Webスクレイピングで急落銘柄通知ツールを作成してみた
どうも、こじまるです。前回急落銘柄を通知するツールを作成しましたが、今回はそちらの続編になります。
急落銘柄ツールをTwitterの仲良しさんに共有したのですが、導入サポートをしている中でユーザが扱うには少し不便な点が見つかりました。そのため、ユーザの利便性を向上させるために、GASを使って急落銘柄通知ツールを再作成することにしました。
1.はじめに
前回作成したツールの不便な点
前回作成したツールはプロセスを定期的に実行させる必要があります。ユーザ環境によっては、常にPC/サーバが起動している状態でない場合もあるため、ユーザの実行環境に依存せず、システムを稼働できるようにしたいと思いました。
また、pythonをインストールしたり、CLIでpipを使ったりしないといけないので、プログラミングをほとんど知らないユーザに使っていただくには少し導入障壁が高いです。そのため、少しでもユーザが導入しやすい環境で構築したいと思いました。
前回作成したツールの不便な点
1. プログラムの実行環境が常に稼働していなければならない
2. ユーザの導入障壁が高い
解決策
上記の解決策として、スクレイピングを行うメイン処理をGAS(Google App Script)で置き換えることにしました。
GASの採用理由
・サーバレスでプロセスを実行させることができること
・プロセスを定期実行することができる
・ユーザは環境構築不要
GASはGoogleのプラットフォーム上で実行されるため、サーバーレスでプロセスを実行させることができます。また、トリガー実行によるスケジュール登録もできるので、スケジューラでプロセスを定期実行する必要もなくなります。それ以外にも、環境構築などもほとんどする必要が無いため、ユーザは導入しやすくなっています。
2. システムの概要
メイン処理は下記のような流れで実行します。下記には記載していませんが、事前にGASをScedulerに登録する必要があります。
メイン処理(定期実行)
メイン処理の流れ
①スクリプトの定期実行
②銘柄情報を取得
③急落銘柄を通知
※今回は導入方法のみ説明し、スクレイピングの内容については説明しません。
3. 導入方法
環境構築
環境構築
1. SpreadSheetの用意
2. Parserライブラリ追加
3. スクレイピングのコードを設定
4. トリガー実行でスケジュール登録
1. Spread Sheetの用意
下記のSpread Sheetは事前処理済みのSpread Sheetになります。(詳細はこちら)
[Spread Sheet]->[ファイル]->[ダウンロード]->[Microsoft Excel(.xlsx)]より、Excelファイルをダウンロードします。
次に、所有するGoogleアカウントでログインし、ドライブにExcelファイルをアップロードしてください。[ドライブ]->[新規]->[ファイルのアップロード]より、Excelファイルを選択することでファイルをアップロードすることができます。
ドライブにExcelファイルとしてアップロードされた状態になります。Excelファイルにアクセスし、[ファイル]->[Googleスプレッドシートとして保存]を選択し、Googleスプレッドシートとして扱える状態にします。
2. Paraserライブラリの追加
Googleスプレッドシートより、[ツール]->[スクリプトエディタ]を選択します。すると、下記のようなエディタが表示されます。
Parserライブラリが必要になりますので、下記の記事を参考に設定してください。
3. スクレイピングのコードを設定
コードは下記になりますので、スクリプトエディタに貼り付けます。
// 急落率
TARGET_DROP_RATE = 0.5;
function isHoliday(date) {
if (date.getDay() === 0 || date.getDay() === 6) {
return true;
}
// 日本の祝日カレンダーに終日予定があれば祝日とする
var calendar = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com');
var events = calendar.getEventsForDay(date);
return events.length > 0;
}
function createTriggers() {
console.log('createTriggers');
var now = new Date();
// 残っているトリガーを掃除する
var triggers = ScriptApp.getProjectTriggers();
if (Array.isArray(triggers)) {
triggers.forEach(function(trigger) {
// mainのトリガーのみを削除する
if(trigger.getHandlerFunction() === 'main') {
ScriptApp.deleteTrigger(trigger);
}
})
}
// 土日祝はスケジュールしない
if (isHoliday(now)) {
console.log('there are no schedules today');
return;
}
var hours = ["9:30", "10:00", "10:30", "11:00", "11:30", "12:30","12:45", "13:00", "13:30", "14:00", "14:30"];
hours.forEach(function(time) {
var date = new Date();
var array = time.split(':');
var hour = parseInt(array[0]);
var minute = parseInt(array[1]);
date.setHours(hour);
date.setMinutes(minute);
if (now.valueOf() < date.valueOf()) {
// main() のトリガーを指定した日時で作成
ScriptApp.newTrigger("main").timeBased().at(date).create();
}
})
}
function main() {
console.log('hello.')
myFunction()
}
function fetch_element(html, from_element, to_element){
return Parser.data(html).from(from_element).to(to_element);
}
function substitute(str, delimiter_list){
for(var value of delimiter_list){
str = str.replace(value, '');
}
return str;
}
function send_message(message){
var url = "https://hooks.slack.com/XXXXXXXXXX"; // <- 変更が必要
var payload = {
"text":message,
'icon_emoji':':squirrel:',
'username':'StockBot'
};
var params = {
"method" : "POST",
"payload" : JSON.stringify(payload)
};
UrlFetchApp.fetch(url, params);
}
function myFunction() {
const workbook = SpreadsheetApp.getActive();
const sheet= workbook.getSheetByName("stockBot");
const lastRow = sheet.getLastRow();
const values = sheet.getRange("A2:E").getValues();
let dt_now = new Date();
dt_month = dt_now.getMonth() + 1;
dt_now.setMonth(dt_now.getMonth() + 1);
dt_next_month = dt_now.getMonth() + 1;
var list = [];
for (let i = 0; i < lastRow - 1; i++) {
rowData = values[i];
var stock_code = rowData[0] // コード
var stock_name = rowData[1] // 銘柄名
var stock_vesting_date = rowData[2] // 権利確定月
if(stock_vesting_date == "随時"){
continue;
}else{
var pattern = /\d+月/g;
var month_list = stock_vesting_date.match(pattern);
var array = []
month_list.forEach(function(value,i){
array[i] = substitute(value, [/月/g])
});
if(array.includes(String(dt_month)) || array.includes(String(dt_next_month))){
console.log(stock_code);
}else{
continue;
}
}
try{
var url = "https://minkabu.jp/stock/" + stock_code + "/chart";
var html = UrlFetchApp.fetch(url).getContentText("UTF-8");
console.log("銘柄コード : " + stock_code);
// 現在値
var integer_str = fetch_element(html, '<div class="stock_price">', '<span class="decimal">').build();
var decimal_str = fetch_element(html, '<span class="decimal">', '</span>').build();
var current_price = parseFloat(substitute(integer_str,[/\,/g,/ /g,/\n/g]) + decimal_str);
// 株価
var stock_table = fetch_element(html, '<table class="md_table md_table_vertical">', '</table>').build();
var stock_list = fetch_element(html, '<tr>', '</tr>').iterate();
// 前日終値
var previous_closing_place_str = fetch_element(stock_list[0], '<td class="num">', '</td>').build();
var previous_closing_place = parseFloat(substitute(previous_closing_place_str,[/\,/g,/円/g]));
// 急落率
price_drop_rate = (1 - current_price / previous_closing_place) * 100;
console.log(price_drop_rate);
if(price_drop_rate >= TARGET_DROP_RATE){
dict = {'コード':stock_code, '名前':stock_name, '現在値':current_price, '前日終値':previous_closing_place, '急落率':price_drop_rate.toFixed(2)};
list.push(dict);
}
}catch(e)
{
console.error( "エラー:", e.message );
}
}
message = "";
for(message_dict of list){
message += `【銘柄名:${message_dict["名前"]}】(コード:${message_dict["コード"]})\n`;
message += `現在値:${message_dict['現在値']}\n`;
message += `前日終値:${message_dict['前日終値']}\n`;
message += `急落率:${message_dict['急落率']}%\n\n`;
}
send_message(message);
}
急落率(TARGET_DROP_RATE)は都合がいい値に変更してください。
// 急落率
TARGET_DROP_RATE = 0.5;
また、下記のURLはSlackのIncoming Webhooksを設定する必要があります。
function send_message(message){
var url = "https://hooks.slack.com/XXXXXXXXXX"; // <- 変更が必要
var payload = {
...
詳細はこちらでは割愛しますので、必要であれば、下記を参考にしてください。
参考
https://developers.wonderpla.net/entry/2020/06/18/110005
上記の変更後、Slackに通知が飛ぶことを確認する必要があります。そのため、下記のようにmyFunction(メイン処理)を選択し、実行ボタンを押下することで実行できます。
4. トリガー実行でスケジュール登録
最後に、定期実行させるためにスケジュール登録を行います。スクリプトエディタより、[トリガー]->[トリガーを追加]を選択します。
ポップアップが表示されるため、下記のように指定してください。
これにより、午前0時~1時の時間にcreateTriggers関数が実行され、myFunctionが定期(下記の時間に)実行されるようトリガー登録されます。
var hours = ["9:30", "10:00", "10:30", "11:00", "11:30", "12:30","12:45", "13:00", "13:30", "14:00", "14:30"];
4. まとめ
GASを使ってWebスクレイピングを行うツールを再作成してみました。始めてGASでコードを作ってみましたが、割と作りやすかったです。サーバレスで自動実行できるので、ものすごく便利ですね。もし、興味がありましたら活用してみてください。
この記事が気に入ったらサポートをしてみませんか?