ADVERTISEMENT

【Googleスプレッドシート】Apps Scriptで配列の要素ごとに処理を並列化!UrlFetchAllの活用

【Googleスプレッドシート】Apps Scriptで配列の要素ごとに処理を並列化!UrlFetchAllの活用
🛡️ 超解決

スプレッドシートから複数のAPIを呼び出してデータを取得する場合、一つずつ順番にリクエストを送ると処理に時間がかかります。そんなときに役立つのがUrlFetchApp.fetchAllです。このメソッドを使えば、複数のリクエストを並列で実行し、全体の所要時間を大幅に短縮できます。この記事では、配列の各要素に対して並列処理を行う方法を具体的なコードとともに解説します。

【要点】UrlFetchApp.fetchAllで配列処理を高速化するポイント

  • requestオブジェクトの配列を用意: URLやメソッド、ペイロードなどを各要素に設定し、fetchAllに渡します。
  • 並列実行による高速化: 複数のリクエストを同時に送信するため、逐次処理より数倍速くなります。
  • レスポンスの順序は保持: リクエストの配列順に対応するレスポンスが返ってくるため、処理が容易です。

ADVERTISEMENT

UrlFetchApp.fetchAllが並列処理を実現する仕組み

UrlFetchApp.fetchAllは、引数としてHTTPリクエストオブジェクトの配列を受け取り、それらを同時に実行してレスポンスの配列を返します。内部的にはGoogleのサーバー側で複数のHTTPリクエストが並行して処理されるため、逐次処理に比べて劇的に実行時間が短縮されます。ただし、一回の呼び出しで扱えるリクエスト数は最大100件までという制限があります。また、各リクエストのタイムアウトは30秒ですので、長い処理が必要な場合は注意が必要です。

UrlFetchApp.fetchAllを使った並列処理の手順

以下の手順で、配列の各要素に対して並列HTTPリクエストを実行します。ここでは、スプレッドシートのA列にURLが並んでいるケースを想定します。

  1. リクエストオブジェクトの配列を用意する
    まず、各リクエストの設定をオブジェクトとして準備します。最低限urlプロパティが必要です。必要に応じてmethodやheaders、payloadなどを指定します。例えば、次のコードでURLリストからリクエスト配列を作成します。
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    var urls = sheet.getRange('A1:A10').getValues().flat();
    var requests = urls.map(function(url) {
      return {url: url, muteHttpExceptions: true};
    });
  2. fetchAllメソッドを呼び出す
    用意した配列をUrlFetchApp.fetchAllの引数に渡します。戻り値としてHTTPResponseの配列が返ります。
    var responses = UrlFetchApp.fetchAll(requests);
  3. レスポンスを処理する
    responses配列はrequestsと同じ順序です。各レスポンスからgetContentText()などでデータを取得し、スプレッドシートに書き込みます。例えば、次のようにループ処理を行います。
    responses.forEach(function(response, index) {
      if (response.getResponseCode() === 200) {
        var data = response.getContentText();
        sheet.getRange(index + 1, 2).setValue(data);
      } else {
        sheet.getRange(index + 1, 2).setValue('エラー: ' + response.getResponseCode());
      }
    });

上記の例では、muteHttpExceptionsをtrueに設定しているため、HTTPエラーが発生しても例外がスローされず、レスポンスオブジェクトからステータスコードを取得してエラー処理を行えます。

UrlFetchApp.fetchAll使用時の注意点とよくある失敗

リクエスト数が100件を超えるとエラーになる

fetchAllは一度に最大100件のリクエストしか受け付けません。それ以上を送信したい場合は、配列を分割して複数回呼び出す必要があります。例えば、200件の場合は100件ずつ2回に分けて実行します。次の関数は、任意の数のリクエストをチャンクに分割して処理します。

function fetchAllInChunks(allRequests) {
  var chunkSize = 100;
  var allResponses = [];
  for (var i = 0; i < allRequests.length; i += chunkSize) {
    var chunk = allRequests.slice(i, i + chunkSize);
    var responses = UrlFetchApp.fetchAll(chunk);
    allResponses = allResponses.concat(responses);
  }
  return allResponses;
}

タイムアウトによる失敗

各リクエストのタイムアウトは30秒です。応答が遅いサーバーに対しては、事前にタイムアウト時間を延ばせないか検討するか、リクエスト自体を分割して対策します。また、実行時間制限(単純トリガーで6分、カスタムトリガーで30分)にも注意してください。

レスポンスの順序が保証される

リクエスト配列の順序とレスポンス配列の順序は一致します。この性質を利用して、インデックスで元データと結果を紐付けられます。上記の例では、forEachのindexを使ってA列のURLとB列の結果を対応させています。

エラーハンドリングの方法

muteHttpExceptionsをtrueに設定しないと、ステータスコードが400以上のレスポンスが例外をスローします。trueに設定すれば、レスポンスオブジェクトを取得できるので、getResponseCode()でエラーを判別できます。また、ネットワークエラー(DNS解決失敗など)は例外として発生するため、try-catchで囲むと安全です。

ADVERTISEMENT

逐次処理とfetchAllの比較

項目 逐次処理(UrlFetchApp.fetch) 並列処理(UrlFetchApp.fetchAll)
実行速度 リクエスト数に比例して時間が増加 リクエスト数が増えてもほぼ一定(上限100件)
コードの複雑さ シンプルなforループで済む 配列の準備とチャンク分割が必要だが難しくない
エラーハンドリング 各リクエストごとにtry-catch 一括でレスポンスを取得後、個別にステータスコードをチェック
最大リクエスト数 特に制限なし(実行時間制限あり) 1回の呼び出しで100件まで(チャンク分割で対応可能)
メモリ使用量 逐次処理のため少ない 全レスポンスを同時に保持するため多い

まとめ

UrlFetchApp.fetchAllを使うことで、複数のHTTPリクエストを並列に実行し、処理時間を大幅に短縮できます。配列の各要素に対する処理を並列化したい場合、まずリクエストオブジェクトの配列を作成し、fetchAllに渡すだけで実現できます。100件を超えるリクエストがある場合はチャンク分割を行うことで対応可能です。この手法は、外部APIの一括取得や複数のWebサービスからのデータ集約など、バッチ処理の効率を大きく向上させます。例えば、株価APIの一括取得や複数のRSSフィードの同時取得などに活用してみてください。


ADVERTISEMENT

この記事の監修者
✍️

超解決 第一編集部

疑問解決ポータル「超解決」の編集チーム。正確な検証と、現場視点での伝わりやすい解説を心がけています。