Google Apps ScriptでWebAPIや外部サイトを取得する指南書【GAS】
Google Apps Scriptは便利な事に外部のサイトへアクセスして情報をサーバサイドで取得させる事が可能です。但し、イントラネット内の情報はアクセスできません(かつてはそういうオプションもありましたが)。取得した情報を元にさらに、Drive内にファイルを生成したり、またGoogle Apps Script Execution APIと併用して、代理でクライアントに返すなど、様々な使い方ができます。
その際に使用するのがUrlFetchAppクラスですが、G Suite内ではPDFの作成、外部Web APIへアクセス、スクレイピング、外部の関数を叩いて発火させる、トリガーで自動収集等など。但し、使いみちが広いですが1日あたりの利用上限がありますので、あまり短い間隔で作動させないように注意が必要です。
目次
今回使用するスプレッドシート
開くと、メニューが出るので、「作業実行」から始めます。
UrlFetchAppのリファレンス
UrlFetchAppはurlを入れるだけでもアクセスは出来ますが、相手先のウェブサービスやサーバーの条件に応じて様々なオプションを追加してあげる必要性があります。以下に簡単にそのオプション事例を記述してみました。
- contentType ・・・コンテンツの種類(例:"contentType" : 'application/xml; charset=utf-8')
- headers ・・・ヘッダー情報。ウェブAPIなどで別途ヘッダーに含めるべき情報は記載。
- method ・・・実行するメソッド。GETやPOSTなど(例:"method" : "POST")
- payload ・・・送信時に合わせて送る情報を記載。IDやPASSなど(例:payload:{"user_id" : "userid", "password" : "pass"})
- validateHttpsCertificates ・・・ falseにするとSSL認証エラーを回避可能
- followRedirects ・・・リダイレクト先の情報を追跡するかどうか?(デフォルト:true)
- muteHttpExceptions ・・・エラーを捕捉するかどうか?(デフォルト:false)
- escaping ・・・URLに於いてエスケープ処理をするかどうか?(デフォルト:true)
OAuth2.0認証などでAccess Tokenを使用する事がありますが、これらは全てheadersに含めるようになっています。主な例は'Authorization': 'Bearer '+ accessTokenといったような書き方。GASでもPDF生成時には必要な処理です。
UrlFetchApp(url,options)としてアクセスさせますが、optionsは別に切り出して於いて、また、payload内容も別に切り出しておくとコードが綺麗になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//payload情報 var payload = { "user_id" : "userid", "password" : "pass" } //header情報 var headers = { 'Cookie' : cookies }; //urlfetchappのオプション情報 var options = { "method" : "POST", "headers" : headers, //header情報を追加 "payload": payload, //payload情報を追加 "muteHttpExceptions" : true } //外部へアクセスさせる var res = UrlFetchApp.fetch(url, options); |
様々な使い方
REST APIにPOSTする
かつてWeb APIと呼ばれていたものですが、現在は様々なウェブサービスでREST APIがサポートされています。会計ソフトのfreeeであったり、メッセージアプリのLINEなどなど業務用から個人用まで様々なウェブサービスでAPIは当たり前のように実装されており、外部アプリケーションから簡単に叩けるようになっています。
これらに対して、Google Apps ScriptからもPOST通信などでアクセスし情報を取得したり、命令を実行させたりする事が可能です。例えばLINE Notify APIなどの場合は以下のような形でアクセスします。詳細な方法当についてはまた別エントリーで記述してみたいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//LINE Notifyへメッセージを送る function sendNotify(token,message){ //アクセスさせるエンドポイントURLを指定 var url = "https://notify-api.line.me/api/notify" //オプション指定 var options = { "method" : "post", "payload" : "message=" + message, "headers" : {"Authorization" : "Bearer "+ token} }; //通知を送る UrlFetchApp.fetch(url,options); } |
たった数行ですが、これで特定グループに対して、LINE通知を送りつける事が可能になります。アクセストークンなどはコードに直書きよりは、スクリプトプロパティを使って格納しておくと良いでしょう。
スクレイピングする
ウェブサイトをスクレイピングさせる為にUrlFetchAppを利用してる人が結構多数いるようです。ですが、ウェブサイトをスクレイピングするには、取得したHTML情報を更に分解する必要があり、正規表現やXmlServiceなどを使って、取得した情報に対して処理が必要です。スクレイピングの詳細な手法についてはまた別エントリーで書きたいと思います。
今回はクックパッドのとあるレシピの「idがhistoryのデータ」を抜き出してみたいと思います。スクレイピングはとても面倒なので、今回はこちらのライブラリを使用させていただきました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//簡単スクレイピング function scraper(){ //uiを取得する var ui = SpreadsheetApp.getUi(); //URLにアクセスしてHTMLデータを取得 var url = "https://cookpad.com/recipe/5057227"; var res = UrlFetchApp.fetch(url); //HTMLデータをXMLへ変換し取得 var text = Xml.parse(res, true); var body = text.html.body[2].toXmlString(); //3個目のBodyの中に目的のデータが入ってた var doc = XmlService.parse(body); var xml = doc.getRootElement(); //IDがhistoryのデータを取得する var title = parser.getElementById(xml, 'history'); //取得データを返す ui.alert(title.getValue()); } |
※HTMLをスクレイピングする場合、まずXml.parseで取得した後、toXmlString()にてXML形式に変換してあげる必要があります。
※parser.getElementByIdはライブラリで追加した関数です。
Basic認証サイトを開く
昔ながらのhtpasswdを使ったBasic認証は未だに利用されていますが、UrlFetchAppクラスでアクセスさせる事が可能です。サンプルファイルにもありますが、.htaccessと.htpasswdを生成し配置しておくとことで簡単にBasic認証を装備できます。このディレクトリの中にあるファイルにアクセスする場合には、以下のようなコードを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
//basic認証アクセス function basicauth(){ // アクセス先の情報 var url = "https://officeforest.org/basic/secret.txt"; //認証情報 var ui = SpreadsheetApp.getUi(); var uires = ui.prompt("IDを入れてください"); var userid = uires.getResponseText(); var uires = ui.prompt("PASSWORDを入れてください"); var password = uires.getResponseText(); // POSTメソッドのオプション var options = { "method" : "POST", "headers" : {"Authorization" : " Basic " + Utilities.base64Encode(userid + ":" + password)}, "muteHttpExceptions" : true } try{ //Basic認証をPOSTする var res = UrlFetchApp.fetch(url, options); var content = res.getContentText("UTF-8"); ui.alert(content); }catch(e){ ui.alert(e.meesage); } } |
URLはBasic認証が掛かっているので、通常ではアクセスできません。IDとPASSWORDを入れる事で可能です。UrlFetchAppからはPOSTで送信を行いますが、useridとpasswordは必ず、Utilities.base64Encodeにて処理をしたものを送信します。
optionsでPOSTを指定し、取得できればテキストの中身が取り出せます。失敗するとエラーメッセージが表示される仕組みです。今回のサンプルでは、ID:guestman, PASSWORD:tomatoloveでアクセスが可能です。
注意点
アクセス回数の上限
Google Apps Scriptには特定のクラスについて、プラン毎にアクセス上限が設置されています。トリガーなどと合わせて自動取得させているケースなどに於いて、この上限に引っかかってプログラムがエラーを吐くといった事が、作り方によっては発生してしまいます。UrlFetchAppクラスに関して言えばその上限は通常のGoogleアカウントだと
- 20,000回コール / 1日 (G Suiteだと100,000回コール可能)
- 100MB受信 / 1日 (全プラン共通)
またこれとは別に連続アクセスでおよそ自分が計測した限りでは、同一アカウントで実行時に10秒間に2アクセスが限界(それ以上になると、3回目のアクセスで404エラーが発生する)です。特に注意したいのは、ウェブアプリケーション等で複数の人が同時アクセスする際に、自分のアカウントの権限で動かしてると、運悪くこの10秒間に複数アクセスが集中し、PDFの生成などで制限に引っかかる可能性が非常に高いです。
そこで、この制限を回避する為に、UrlFetchAppを呼び出す手前で、Utilities.sleepを5秒など必ず入れておくと回避しやすくなります。または、アクセスするアカウント毎の権限で動かすのがベストでしょう。
エラーハンドリング
UrlFetchAppでアクセスする場合、必ずしもアクセスが成功するとは限りません。ですので、このクラスを使う場合には、必ずエラートラップを仕掛けて、キャッチして挙げなければそこでストップしてしまいます。相手サーバがビジーであったり、アクセス拒否であったり、様々なエラーが予測されるのですが主に以下のような処置を追加します。
muteHttpExceptionをtrueにする
UrlFetchAppのオプション指定にmuteHttpExceptionがあります。これをTrueにする事で、エラーを捕獲し相手からのレスポンス内容(404エラーだよ等)が取得可能です。これをせずエラーが発生するとスクリプトが停止するので、付けるようにしましょう。
try〜catchを必ず付ける
muteHttpEcceptionでも全てのエラーを捕捉出来るわけではなく、サーバーがビジーであったりアクセス拒否などが発生した場合に、エラーを捕捉する必要があるので、必ずつけまた、その内容をロギング出来るようにしておくとベターです。
実際のコード
実際に上記の2つをカバーするコードは以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//エラー処理対応 function fetchman() { //URLを入力してもらう var ui = SpreadsheetApp.getUi(); var url = ui.prompt("URLを入力してください"); //アクセス情報を設定 var res = ""; var rescode = ""; //オプションを設定する var options = { "muteHttpExceptions" : true, //エラー捕捉 "validateHttpsCertificates" : false, //証明書エラー回避 "followRedirects" : false, //リダイレクト先捕捉 } //アクセス実行 try { //成功時 res = UrlFetchApp.fetch(url, options); onSuccess(url,res.getResponseCode()); } catch(e) { //エラー時 onFail(url.getResponseText(),"",e.message,e.fileName,e.lineNumber,e.stack); } } |
入力されたURLのレスポンス結果は、スプレッドシートのlogシートに書き込まれます。成功時にはログをonSuccess関数で書き込み、失敗時はonFail関数でログを書き込ませています。試しにエラーを発生させると
https://kinoko.tv | 使用できないアドレス: https://kinoko.tv | コード | 24 | at コード:24 (fetchman) |
こんな感じでエラーが書き込まれます。