Google Apps Script (GAS) でスクレイピング~気象庁のページにあるブロックのデータをスプレッドシートに書き込んでマスタを作る実験~

MAC

こんにちは。

今回はさらに前回の続き。Google Apps Script(GAS)を使って、スクレイピングの実験です。

今日の対象はブロック一覧データ。ブロックマスタですね。

前回の最後に、コードの課題として

>都道府県とブロックのID一覧を取得すれば、そのブロックIDの数だけスプレッドシートを作って回すこともできます。

と挙げたと思うのですが、その解決をするためのコードです。

気象庁は「過去のデータ検索」から、エリアごとの過去の気象データを調べることができるんですよね。

今回は、特定の地域の「1日ごとのデータ」をGASを使ってスプレッドシートに自動で書き込むようにしてみます。

Google Apps Script(GAS)とメリットについては前々回記事をご参照ください。

なお、今回参考にしたサイトは前回と同様、こちら。

[ JavaScript] splitの振る舞いとcsvを配列に格納するコード

[JavaScript] splitの振る舞いとcsvを配列に格納するコード | きほんのき
JavaScriptのsplitメソッドは指定した区切り文字で文字列を分割し、配列に格納してくれる。引数には正規表現も指定できる。以下はテキストエリアに入力されたcsv形式のテキストを、splitを利用し二次元配列に収めるコード。

JavaScriptでHTMLタグを削除する正規表現

JavaScriptでHTMLタグを削除する正規表現 - Qiita
RSSを引っ張ってきて表示する時にテキストだけ取得したくて、データに含まれているHTMLタグを削除したかった。 メモ。 ```js var str = '今日はとても良い天気だと思うよ?...

JavaScriptで配列から特定の要素を削除する方法

JavaScriptで配列から特定の要素を削除する方法 - Qiita
# はじめに 配列から特定の要素を削除するにはいくつか方法があります。 単純に先頭の要素を削除するにはshift()で実現できます: ```js var ary = ; conso...

気象庁のページにある過去の気象データをGoogle Apps Script(GAS)でスプレッドシートに書き込む

使うもの

Google スプレッドシート

Google スプレッドシート: ログイン
Google スプレッドシートには、無料の Google アカウント(個人ユーザー向け)または G Suite アカウント(ビジネス ユーザー向け)でアクセスできます。

今回もお世話になります。

他の形式で吐き出すこともできますが、調査対象を簡易マスタにまとめて使いますので今回のスクリプトには必須です。

Google Apps Script

スプレッドシートの個別編集画面の「ツール」から「スクリプトエディタ」という手順で起動しましょう。

Googleスプレッドシートの構造

以下の通り。

[1]「data」シート

image

見出しだけつけておきましょう。

image今回は、気象庁ページの過去気象ダウンロードデータの、都道府県選択後に出てくるブロック選択画面のソースから取り出しています。

※ブロック選択画面の例は右を参照。

ソースを見ると、どうやらブロックを選択するコードは以下のようになっていました。

<area shape="rect" alt="稚内" coords="162,77,191,89" href="../index.php?prec_no=11&block_no=47401&year=&month=&day=&view=" onmouseover="javascript:viewPoint('s','47401','稚内','ワツカナイ','45','24.9','141','40.7','2.8','1','1','1','1','1','9999','99','99','','','','','');" onmouseout="javascript:initPoint();">

最初は一行目からデータを取っていたのですが、ブロックそのもののデータとして、「アメダスのデータか観測台のデータか」「緯度経度」「観測できるデータの種類」などが二行目のjavascriptに記載されていたため、二行目を取得するようにしました。

観測できるデータの種類というのは実は結構重要で、次回にコードを書くことになる「ブロックマスタからシートを生成してデータを蓄積する」ための作業に必須です。

前回も書いていますが、気象庁のデータはエリアごとに微妙に取得できる情報の差異があります。

このため、どのデータが取れ、どのデータがない、というのはマスタとしては持っておくべき情報です。

[2]「master」シート

image

dataシートに書き込むための設定的なものと、GASの速度向上を狙って作ってみた簡易マスタシートです。

setValueやらgetRangeやら、とにかくスプレッドシートAPI関連の呼び出しが多いと実行速度低下であっという間に5分過ぎてエラーになってしまうため、

スプレッドシート側でできる作業は極力スプレッドシートに任せ、setValuesなどを積極的に使ってAPI呼び出し回数を減らしています。

今回の基準はE1セル。

A・B・C列の地方一覧とURLについては、

気象庁|過去の気象データ検索
気象庁|過去の気象データ検索

のソースの一部を整形して取得しています。

※URLはなくても実は問題ないです。その分、prec_noからURLを作り出す必要は出てきますが。

Google Apps Script で検索結果件数を取得するコード

以下、「var bookurl_work = “https://docs.google.com/spreadsheets/●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●”;」の部分は、ご自身のスプレッドシートのURLに変更してください。

function getData_meteologicalms(){
//変数宣言(スプレッドシート側) masterが取得したいデータを記載したマスタ、dataが取得したいデータを取っていくデータシート。
var bookurl_work = "https://docs.google.com/spreadsheets/●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●”;
var book_work = SpreadsheetApp.openByUrl(bookurl_work);
var sheet_data = book_work.getSheetByName("data");
var sheet_master = book_work.getSheetByName("master");
var data_startRow = sheet_data.getLastRow()+1;

//変数宣言(HTML取得関係)
var opt = {"contentType":"text/html;","method":"get"};
var data_html = "";
var content_html ="";
var postText = "";
var searchRow = sheet_master.getRange(1,5).getValue();//地方名を選択する変数
var url_gethtml = sheet_master.getRange(searchRow,3).getValue();

//変数宣言(分割処理と書き込み関係)
var Middle_Arr=[];
var table_Arr = [];
var master_endRow = 0;
var data_endRow = table_Arr.length;
var master_endcolumn = 0;
var precblock = [];
var nextAreaRow = sheet_master.getRange(1,4).getValue();//次のスクリプト動作のため
if(nextAreaRow=="x"){
return;
}

//html取得
data_html = UrlFetchApp.fetch(url_gethtml ,opt);
content_html = data_html.getContentText();
postText = getStringSlice(content_html, '
<map name="point">','</map>

');

//html整形。onmouseover の中にある値がそのままデータベースに使えそうなので今回持ってきています。
postText = postText.replace(/onmouseover\=\"javascript:viewPoint\(/g , "" );
postText = postText.replace(/\'\'/g,"'-'");
postText = postText.replace(/\);\".*javascript:initPoint\(\);\"\>/g,"");
postText = postText.replace(/\
<area shape="" />    postText = postText.replace(/\
<area shape="" />/g,"");
postText = postText.replace(/\r?\n/g,"xxxyyy");//変換先はHTML内に存在しない文字列であれば何でも良いのですが、改行コードを変換。
postText = postText.replace(/xxxyyy(xxxyyy)*/g,"xxxyyy");//空白行を削除
//ブロックコードはHTML取得のためにURLに入れる際、0も含む必要があるため、ここで保護っぽいことを行います。yyyxxはマスターの中に存在しない文字列であればOK
postText = postText.replace(/\'s\'\,\'0/g,"'s',yyyxx");
postText = postText.replace(/\'a\'\,\'0/g,"'a',yyyxx");
postText = postText.replace(/\'/g,"");
postText = postText.replace(/yyyxx/g,"'0");
postText = postText.replace(/xxxyyy/g,"\n");

//Logger.log(postText);
Middle_Arr = postText.split( /\n/g );
table_Arr = [];
for( var i = 0 , l = Middle_Arr.length / 2 ; i < l ; i++ ){
table_Arr[i] = Middle_Arr[i*2+1].split(",");
for( var j = 0 , m = table_Arr[i].length ; j < m ; j++ ){//trimも同時に行っておく
table_Arr[i][j] = table_Arr[i][j].replace( /(^\s+)|(\s+$)/g , ""  );
}
}
table_Arr.pop();//最後にnull行ができるので削除
master_endcolumn = table_Arr[0].length;
master_endRow = table_Arr.length;
precblock = sheet_master.getRange(2,5,master_endRow,2).getValues();
sheet_data.getRange(data_startRow,1,master_endRow,2).setValues(precblock);
sheet_master.getRange(1,5).setValue(nextAreaRow);
sheet_data.getRange(data_startRow,3,master_endRow,master_endcolumn).setValues(table_Arr);
/**/
}

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));
}
}

ループを記述して上記を地方の全数である61回繰り返しても良いのですが、急がなければトリガーを使って1分ごとに実行などで集めても構いません。

次回は、できたマスタをもとに地方ごとのシートを生成、データを集められるようにできればいいなと思っています。

関連記事

Google Apps Script (GAS) でHTML上の検索結果の数字を取ってくる実験

Google Apps Script (GAS) でスクレイピング~気象庁のページにある過去の気象データをスプレッドシートに書き込む実験~

コメント