— javascript, google apps script — 1 min read
仕事でもプライベートでもGoogle Apps Script (以下、GAS) を時々触るのだけど、GASは新しめのECMAScript構文は使えなくてコードがもっさりしがち。
それがどうやらこれまでのRhinoランタイムからV8ランタイムへ移行できるようになったようなので早速試してみた。
参考: V8 Runtime Overview | developers.google.com/
仕事でGASの簡単な解説記事をちょこちょこ書いており、そこに書いたコードをV8ランタイム用にリファクタリングしてみた。
これはホームページの記事一覧から記事のタイトルとURLをスプレッドシートに転記するもの。あくまでもスクレイピングの入門的な内容。(社名に関連するところは置き換えてあるのでこのままでは動かないのはご了承願いたい)
※プログラムにまだそれほど馴染んでいないときに書いたものなので結構突っ込みどころは多い。
function scrapingSample() { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1'); var getUrl = 'http://www.example.com/page/1'; var content = UrlFetchApp.fetch(getUrl).getContentText('UTF-8'); var itemRegexp = new RegExp(/<a href=.*?" class="c\d{1,2}">/g); var item = content.match(itemRegexp); var items = []; for (var i = 0; i < item.length; i++) { var itemURL = item[i] .replace('<a href=','') .replace(/"/g,'') .replace(/class=c\d{1,2}>/,''); items.push([itemURL]); } var titleRegexp = new RegExp(/<title>.*?<\/title>/); var titles = []; for (var j = 0; j < items.length; j++) { var itemCont = UrlFetchApp.fetch(items[j]).getContentText('UTF-8'); var title = itemCont.match(titleRegexp)[0] .replace(/<\/*title>/g,'') .replace(' | 株式会社hogehoge',''); titles.push([title]); } sheet.getRange(2, 1, titles.length, 1).setValues(titles); sheet.getRange(2, 2, titles.length, 1).setValues(items);}
公式ページのEnabling the V8 runtime に書いてある「メニュー」から切り替える方法が本記事執筆時点(2020-2-7)できないようだが、もう一方の方法として記載されているスクリプトマニフェストファイルを編集して移行ができた。
以下、公式ページの手順をGoogle翻訳したもの。
- Appsスクリプトエディターでスクリプトを開きます。
- [ 表示]> [ プロジェクトマニフェストを表示]を選択します。
- 結果の
appsscript.json
マニフェストファイルで、runtimeVersion フィールドをvalueに設定しますV8
。- スクリプトを保存します
引用元: V8 Runtime Overview | developers.google.com/
マニフェストファイルの中身はこうなる。
“runtimeVersion”: “V8″を書き足す。
ちなみに”V8″の部分を”DEPRECATED_ES5″にすれば元に戻せる。
{ "timeZone": "Asia/Tokyo", "dependencies": { }, "webapp": { "access": "ANYONE_ANONYMOUS", "executeAs": "USER_DEPLOYING" }, "exceptionLogging": "STACKDRIVER", "runtimeVersion": "V8"}
function scrapSample() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1'); const content = UrlFetchApp.fetch('http://www.example.com/page/1').getContentText('UTF-8'); const itemRegexp = new RegExp(/<a href=".*?" class="c\d{1,2}">/g); const items = content.match(itemRegexp); const itemList = items.map(item => [item.replace(/<a href="(.*?)" class="c\d{1,2}">/, '$1')] ); const titleRegexp = new RegExp(/<title>.*?<\/title>/); const titleList = itemList.map(url => { const itemCont = UrlFetchApp.fetch(url).getContentText('UTF-8'); return [itemCont.match(titleRegexp)[0].replace(/<title>(.*?) \| (.*?)<\/title>/,'$1')]; }); sheet.getRange(2, 1, titleList.length, 1).setValues(titleList); sheet.getRange(2, 2, itemList.length, 1).setValues(itemList);}
実行結果は同じだけど、だいぶスッキリした。
この内容の場合で大きく変わったのは、
これをfor文でそれぞれ回していたのをmapに変えたことと、アロー関数も使ったので見た目はかなり簡潔になった。
※replace部分もスッキリさせたがここはV8には関係ないので割愛。
ちなみに実行時間が速くなるのではと期待したが、
といった理由によりそれほど大きくは変わらなかった。
もっと時間のかかるスクリプトなら結構差が出るのかもしれない。(未検証)
今回はさらっと試しただけなので使っていないが、クラスやテンプレートリテラルでだいぶ使い勝手が良くなりそう。
今度はもう少し大きなもので試してみよう。