2020年12月15日火曜日

アメダスのデータをGASのWebスクレイピングで毎時間自動取得する。

以前、過去の気象データをWebスクレイピングで取得するGASを作成しましたが、今回は1時間おきに更新されるアメダスのデータを取得するGASを作成してみます。

現状、ビニールハウス内のデータ(温度・湿度)についてはNature Remoから取得しているのですが(湿度の精度については課題ありですが)、ハウス外のデータについてはリアルタイムでの把握ができていませんでした。

野外となりますと、防水・防塵への対応なども必要となりますので、どう実現したものかと思案していたのですが、よく考えたらわざわざ自分で測定しなくてもちょっと離れた位置のデータならあるよなと。アメダスが。毎正時の観測値が各地点ごとに表形式で更新されています。(今回使いませんが地図形式もあります。)

個人的な好みとしても、ハードは最小限にとどめたいので一石二鳥です。使えるものは使っていきましょう。

ということで、以下、作り方のメモです。

(ちなみに、Webスクレイピングというのはウェブサイトから情報を抽出する技術です。公式で適切なAPIが提供されていれば不要ですが、そういうところばかりとは限りません。やはり、使えると便利です。サイトによっては規約等で禁止されていることもあるので注意が必要です。)


① スプレッドシートの準備

まずはスプレッドシートの準備をします。


画像の通り、1行目に見出しを作ります。A列から順に、「日時」「気温(℃)」「降水量(mm)」「風向(16方位)」「風速(m/s)」「日照時間(h)」「湿度(%)」「気圧(hPa)」です。

これは小名浜の場合ですが、地点によって取得できる要素が異なるので適宜修正してください。

シート名は「data」にしておきます(コード作成時にこの名前で使います)。


② コードの作成

作成したスプレッドシートの「ツール」→「スクリプト エディタ」からスクリプトファイルを作成します。コードは以下の通りです。

var spreadsheet = SpreadsheetApp.openById("スプレッドシートID");
var dataSheet = spreadsheet.getSheetByName("data");
  
function getAmadasToday() {
  var dataLastRow = dataSheet.getLastRow();
  
  var opt = {"contentType":"text/html;","method":"get"};
  var dataAmd = "";
  var contentAmd = "";
  var postText = "";
  var urlAmd = "アメダスURL";
  
  var date_and_time = new Date();
  var dataDate = Utilities.formatDate(date_and_time, "JST", "YYYY/MM/d H");
  var time = Utilities.formatDate(date_and_time, "JST", "H");
  if(time == 0) {
    time = 24;
  }

  var middleArr = [];
  var tableArr = [];

  dataAmd = UrlFetchApp.fetch(urlAmd ,opt);
  contentAmd = dataAmd.getContentText();
  postText = getStringSlice(contentAmd, '<td class="time left">' + time + '</td>','</tr>');
  postText = postText.replace(/<\/td>/g, '</td>,');
  postText = postText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, '');
  postText = postText.replace( /\r\n/g , "\n" );
  postText = postText.replace( /^(\n+)|(\n+)$/g , '');
  postText = postText.replace(/\/\/\//g, '');
  postText = postText.replace(/&nbsp;/g, '');
  middleArr = postText.split( /\n/g );
  tableArr[0] = middleArr[0].split(",");
  tableArr.splice(0,0);
  
  if(tableArr[0][0] != "" && dataDate != dataSheet.getRange(dataLastRow, 1).getValue()) {
    dataSheet.insertRows(dataLastRow + 1); 
    dataSheet.getRange(dataLastRow + 1, 1).setValue(dataDate);
    dataSheet.getRange(dataLastRow + 1, 2, 1, 8).setValues(tableArr);
  }
}

function getStringSlice(content, startStr, endStr) {
  var indexStart = content.indexOf(startStr);
  if(indexStart == -1){
    return "";
  } else {
    indexStart += startStr.length
    return content.slice(indexStart, content.indexOf(endStr, indexStart));
  }
}

書き換えが必要なのは3か所です。

・1行目「スプレッドシートID」

スプレッドシートを開いた状態でURLの下記部分に表示されます。ちょっと長めです。

https://docs.google.com/spreadsheets/d/ここの値をコピペ/edit#gid=0


・11行目「アメダスURL」

https://www.jma.go.jp/jp/amedas_h/ より取得する地点を選んでください。

小名浜の場合ですと、 https://www.jma.go.jp/jp/amedas_h/today-36846.html になります(?より後ろにくっついている文字は必要ありません)。


・39行目「8」

取得する要素数によって変わります。要素数+1の値を入れてください。


GAS初回実行時は承認が必要となります。無料のGoogleアカウントの場合「このアプリは確認されていません」というページが表示されます。ここで「安全なページに戻る」をクリックすると実行できませんので、「詳細」→「プロジェクト名(安全ではないページ)に移動」から承認します。


③ トリガーの設定

定期的にmain関数を実行するためにトリガー設定をします。

「トリガーを追加」より、

・「実行する関数を選択」→ getAmadasToday
・「実行するデプロイを選択」→ Head
・「イベントのソースを選択」→ 時間主導型
・「時間ベースのトリガーのタイプを選択」→ 分ベースのタイマー
・「時間の間隔を選択(分)」→ 30 分おき
・「エラー通知設定」→ 今すぐ通知を受け取る


アメダスの更新は1時間おきで、毎時間10分頃までには更新されるようですが、時間ベースのトリガーでは実行する厳密な時間は指定できないため、アメダスが未更新だった場合に備えて「30 分おき」にしておきます。実行時に最新の値が未取得だった場合のみスプレッドシートへの入力が行われます。


④ 取得開始


このような感じになります。うまく動きましたでしょうか。


***

GASによるWebスクレイピングのスクリプトは一度作っていたのでけっこう楽に作れました。

1時間おきの気象データが(概ね)リアルタイムで取得できるといろいろ使えますね。まず温度・湿度の変化を確認するグラフは作っておきたいところ。

Nature Remoの湿度の精度がいまいちなのでこちらで補完できたらと考えています。


・関連投稿
気象データをGASのWebスクレイピングで自動取得する。

Nature RemoとGASを使って温度・湿度をリアルタイムで記録する。

GASで動的な栽培管理用データ閲覧Webページを作る。


Twitter(@nkkmd)、Instagram(@nkkmd)も日々更新中です。