GASで別のスプレッドシートをID指定(openById)で開く方法

GAS
目次

Google Apps Script(GAS)で日々の集約作業やデータ連携を組んでいると、スクリプトを実行しているシートそのものではなく、別部署が管理している元帳シートや、前月分のログが保存されている外部ファイルを直接読みに行きたい場面が頻繁に出てきます。スプレッドシートにスクリプトを紐付けるコンテナバインド型だけでなく、単体で動作するスタンドアロンスクリプトでも、GASから外部スプレッドシートを開いてID指定で特定のファイルを取得する手法はデータ連携処理の土台になります。ここでは一番基本となるopenByIdを使った取得ロジックと、実務でトラブルになりやすい実行権限やシート取得のズレを回避するための実装パターンを詳しく整理します。

openByIdで対象ファイルを取得してシートを操作するコード

外部のファイルへアクセスする最小限の構成は次の通りです。URLから切り出したシート固有のIDを渡し、取得したスプレッドシートオブジェクトから目的のシート名を取り出してログに出力します。

function openTargetSpreadsheet() {
  // 連携先スプレッドシートのIDを指定(URLの /d/ と /edit の間の文字列)
  const targetFileId = "SPREADSHEET_ID";
  
  try {
    // IDを指定して外部スプレッドシートを取得
    const ss = SpreadsheetApp.openById(targetFileId);
    
    // 目的のシートを取得
    const sheet = ss.getSheetByName("シート1");
    
    if (!sheet) {
      Logger.log("指定されたシート名が見つかりませんでした。");
      return;
    }
    
    // 取得したシートの情報を確認
    Logger.log("開いたシート名: " + sheet.getName());
    Logger.log("最終行: " + sheet.getLastRow());
    
  } catch (e) {
    Logger.log("スプレッドシートを開く処理でエラーが発生しました: " + e.message);
  }
}

この処理は、日次集計用シートから各支社ごとの個別ブックを参照しにいったり、マスターデータが保管されているマスタースプレッドシートから品名や単価の対応表を引っ張ってきたりするときに使います。現在実行中のシートを暗黙的に指すSpreadsheetApp.getActiveSpreadsheet()では自ブック以外の情報に触れられないため、別ブックのURLやIDを明示的に指定して参照先を切り替える必要があります。

実装時にまずずれるポイントとなるのが、スプレッドシートIDの指定ミスです。スプレッドシートのURLは「https://docs.google.com/spreadsheets/d/◯◯◯/edit#gid=0」という構造になっており、「/d/」の直後から「/edit」の直前までにある英数字と記号の羅列が固有のファイルIDに該当します。末尾にある「#gid=0」などのパラメータまで一緒に文字列へ含めてしまうと、GAS側は該当するファイルを見つけられず「Exception: スプレッドシートが見つかりません」や「無効な引数」といったエラーを投げて処理が中断します。IDをコード内に直接ベタ書きする場合は、前後のスラッシュが含まれていないかを念入りに確認することが大切です。

また、このIDは環境や開発フェーズによって頻繁に差し替わる性質を持っています。コードの先頭に定数として文字列を埋め込んでも動きますが、本番環境と検証環境でファイルが分かれている運用では、コードを書き換えるたびに差分管理が煩雑になります。業務で安定して動かす回避策としては、PropertiesServiceを利用してスクリプトプロパティに対象ファイルのIDを保存しておき、コード内では「PropertiesService.getScriptProperties().getProperty(‘MASTER_SS_ID’)」のように呼び出す形にしておくと、コードそのものに手を加えることなく接続先を安全に切り替えられます。

getActiveSpreadsheetとopenByIdの違いによるスコープと権限のズレ

コンテナバインドスクリプトで開発を始めた人が最初につまずきやすいのが、getActiveSpreadsheet()とopenById()の間にある「操作コンテキストの違い」です。自ファイルだけを操作しているときは、開いているシートのUI上でスクリプトが動くため、誰が実行してもそのシートの権限内でスムーズに動作します。しかし、openByIdを使って別の外部ファイルを指定した瞬間、スクリプトを実行しているGoogleアカウント自身が、指定先の外部ファイルに対してもアクセス権(閲覧者または編集者)を持っているかどうかが厳密に問われるようになります。

テスト段階では自分がオーナーである2つのシート間でコードを動かしているためエラーに気付きにくいですが、チームメンバーにスクリプトの実行を委託したり、自動実行トリガーを設定してサービスアカウントや別のアカウント経由で実行されたりした途端、「アクセス権がありません」という例外が発生して停止します。これはコードの問題ではなくGoogleドライブ上の共有権限の問題です。openByIdで開く対象のスプレッドシートには、実行者全員(またはトリガー実行者のアカウント)に対するアクセス権をあらかじめ付与しておく必要があります。

さらに、スタンドアロンスクリプト(Googleドライブ上で単独のファイルとして作成されたGASプロジェクト)からスプレッドシートを扱う場合は、そもそも「アクティブなシート」という概念が存在しないため、常にopenByIdなどの明示的なメソッドを使ってファイルオブジェクトを掴まなければなりません。マクロ記録から派生したコードはgetActiveSpreadsheet()に依存しているケースが多いですが、外部連携を前提としたバッチ処理やワークフローを組み立てる際は、最初から対象ブックをopenByIdで明確に特定してインスタンス変数に保持する書き方に統一しておくと、スタンドアロン化やトリガー運用の移行時にコードの書き換えが不要になります。

openByUrlと比較した際のopenByIdの堅牢性と引数管理

SpreadsheetAppクラスには外部ファイルを開く手段として、IDを指定するopenById()のほかに、完全なURL文字列を渡すopenByUrl()というメソッドも用意されています。ブラウザのアドレスバーからコピーしたURLをそのまま放り込めるため手軽に見えますが、保守性や堅牢性の観点からはopenByIdを選択するのが定石です。

URL全体を引数に取るopenByUrlの場合、ブラウザの共有メニューからコピーした際に付与される「?usp=sharing」のようなトラッキング用パラメータや、最後に開いていたシートの「#gid=123456789」といったアンカー情報が末尾に含まれがちです。これらの余計な文字列が付いたままでもGAS側で正常にファイルを解釈してくれるケースはありますが、URLの形式変更や不完全な文字列切り貼りによって参照解決に失敗するリスクが常に付きまといます。一方でopenByIdは純粋なファイル識別子のみを渡す仕様であるため、末尾のパラメータによる予期せぬ解釈のブレが発生しません。

また、文字数の観点でも大きな差が出ます。GoogleドライブのファイルIDはおよそ44文字程度の英数字で構成されていますが、URL全体になるとドメインや各種パスが含まれるため80〜100文字近くまで膨らみます。設定ファイルや前述のスクリプトプロパティ、あるいは管理用のマスタースプレッドシートに連携対象のファイル情報を一覧化して保持する場合、無駄なURL文字列を削ぎ落としてIDのみで管理した方がデータの視認性が向上し、入力ミスや改行の混入を防ぎやすくなります。チーム内でGASスクリプトを共有運用する際も、「URL全体ではなく/d/の間のIDを登録する」という共通ルールを設けておくと、データ管理のブレを最小限に抑えられます。

ループ処理内でopenByIdを連続呼び出しする際のオーバーヘッドと制限

外部スプレッドシートからデータを取得する処理を組む際、最もパフォーマンスを悪化させやすいのが「ループ処理の中でopenByIdを繰り返し実行してしまう設計」です。たとえば、支社別の売上シートが50ファイル存在し、それぞれのファイルから今月の合計値を取得したいといった要件のとき、以下のような書き方をしてしまうケースが見られます。

各支社のIDが並んだ配列をforループで回し、そのループ内部で毎回「const ss = SpreadsheetApp.openById(id);」を呼び出し、シートを開いてセルの値を取得して閉じる、という処理を連続で行うと、実行時間が極端に跳ね上がります。GASにおけるスプレッドシートのオープン処理は、Googleのサーバー内部でドキュメントのメタデータをロードし、内部キャッシュを構築するための重い通信プロセスを伴います。ファイル1つを開くのに0.5秒から2秒程度のレイテンシが発生するため、数十個のファイルを愚直にopenByIdし続けると、GASの実行時間上限(無料アカウントでは6分)に到達して処理が強制終了してしまいます。

この問題を回避するためには、開くファイル自体の数を減らすか、処理の頻度とアーキテクチャを見直す必要があります。すべての支社ファイルから1件ずつ値を取りに行く「プル型」の構造にするのではなく、各支社シート側の更新トリガーを使って集約用の中央シートにデータを書き込ませる「プッシュ型」の設計に改めるか、どうしても集計スクリプト側から巡回する必要があるなら、1回のトリガー実行で処理するファイル数を数件に制限し、実行ステータスをプロパティに保存しながら時間差トリガーでバトンを渡していく分散実行ロジックを検討しなければなりません。openByIdは手軽に外部ファイルを扱える強力なメソッドですが、API呼び出しと同等の通信負荷がかかっているという前提を意識しておく必要があります。

getSheetByNameとの組み合わせで発生するnullエラーへの対抗策

openByIdを使ってスプレッドシートオブジェクトを問題なく取得できたとしても、その直後に呼ぶgetSheetByName()の段階でスクリプトがクラッシュするトラブルは非常に多く発生します。タイトルの主語である「別のスプレッドシートを開く」という状況下では、対象シートを自分以外の第三者が日常的に編集しているケースがほとんどだからです。

たとえば「シート1」というシート名がある前提でコードを組んでいたとしても、別部署の担当者がシート名を「202404_売上」に変更してしまったり、誤って前後に全角・半角のスペースを入れてしまったりすると、getSheetByName(“シート1”)はエラーを出さずに「null」を返します。この挙動に気づかず、戻り値に対してそのまま「sheet.getLastRow()」や「sheet.getRange()」を呼び出すと、「TypeError: Cannot read properties of null (reading ‘getLastRow’)」という致命的な例外が発生し、スクリプト全体の処理がそこで完全にストップしてしまいます。

この事態を防ぐための実践的な防衛策は、getSheetByNameの直後に必ずnullチェックのガード節を挟むことです。シートが見つからなかった場合に即座にエラーで落ちるのではなく、Logger.logやSlack通知などで「どのスプレッドシートIDの、どのシート名が見つからなかったか」を明示的に出力し、処理をスキップするか安全に終了させる構成にします。また、シート名が変更されるリスクが極めて高い運用の場合は、シート名で特定するのではなく「ss.getSheets()[0]」のようにインデックス番号を指定して1番目のシートを確実に掴む方法や、シートのGID(シートごとに割り振られる固有の数値ID)をあらかじめ控えておき、全シートをループしてGIDが一致するものを走査するようなアプローチを採ることで、外部ファイルの編集作業に左右されない堅牢なデータ連携を実現できます。

タイトルとURLをコピーしました