情シス仕事の備忘録

自身の備忘録を兼ねて、情シス仕事で役に立ちそうな情報を掲載しています

Office Script : API呼出しによるデータ検索(Google Books APIs)

Office ScriptでのAPI呼出しによるデータ検索の例を紹介します。APIはGoogle Books APIsのボリューム検索(リスト)を使用します。

以前の記事で触れた通り、Office ScriptからのAPI呼出しについては、OAuth2認証を要するAPIは使用できないようなのですが、今回使用するAPIはOAuth2認証を使用しないため、Office Scriptからの実行が可能です。

・操作環境:
 ・OS:Windows 11
 ・Webブラウザー:Edge
・使用プラン:Microsoft 365 Business Premium

 

 

この記事で使用するAPIについて

以前の記事で、書籍のISBNコードをバーコードリーダーで読み取り、Google Books APIsで詳細情報を取得し、登録するアプリの作成例を紹介しました。そのアプリではボリューム検索のGETを使用しましたが、今回は複数データを取得できるリストの方を使用します。
※Google Books APIs 公式リファレンス>ボリューム検索(リスト):
 ボリューム: list  |  Google Books APIs  |  Google for Developers


また、この例で使用するパラメーターは以下の通りです。
・q:全文検索クエリ(書籍名や説明等のキーワード)
・maxResults:一度に返される結果の上限件数。今回は40とします
・startIndex:返される最初の結果のインデックス(0から始まる)。

Google Books APIsに限らず、リスト検索では一度に返すデータ件数の上限が設けられているのが一般的です。
例えば、200件該当データがあり、maxResults=40とした場合、インデックスは以下の通りとなります。
・一回目:startIndex=0(設定なしでもよい) ⇒ インデックスが0~39のデータを取得
・二回目:startIndex=40 ⇒ インデックスが40~79のデータを取得
・三回目:startIndex=80 ⇒ インデックスが80~119のデータを取得
・四回目:startIndex=120 ⇒ インデックスが120~159のデータを取得
・五回目:startIndex=160 ⇒ インデックスが160~199のデータを取得

 

PostmanでAPIの動作確認を行う(推奨)

いきなりOffice Scriptで実装するのではなく、先にAPI開発ツール「Postman」でデータのやりとりを確認することをおススメします。Postmanの入手方法は以前の記事を参考にしてください。
以下画面はapiというキーワードで、検索結果のインデックス80から40件取得する例です。

 

検索結果を反映するExcelファイルとOffice Scriptを用意する

検索結果出力用のExcelファイルは、SharePointサイトのドキュメントの下に配置しました。

 

ファイルを開き、出力先シートとして[Book_Search_Output]シートを用意します。各セルは値なしで問題ありません。
[自動化]タブを選択し、[新しいスクリプト]を選択します。

 

コードエディターを左側に広げてスクリプトを記述していきます。
ちなみに、Office Scriptのファイルは保存先を指定しなければ各自のOneDrive内に保存されます。

 

出力元シートとして[Book_Search_Input]シートを用意します。
キーワード入力欄を作成します。
先ほどのスクリプトについて、[ブックに追加]を選択し、実行ボタンを用意します。

 

Office ScriptでAPIに接続しデータ検索する

スクリプトの中身です。ポイント事項は以下の通りです。

  • Office ScriptはAny型が使用できないため、検索結果のJsonデータはInterfaceで定義した型で成形している
  • [全件数]÷[一度に返す件数の上限]の商をページ数とし、ページ数分APIを実行し、全件数分のデータを出力している。全件数は、APIレスポンスの[totalItems]で分かる
  • APIの初回実行時に該当データがなかった場合、コンソールにその旨を出力して処理を終了している
  • API実行はfetch関数を使用し、検索結果のJsonデータをスクリプトの最初に定義したInterfaceの型に格納している。これらはawaitにより結果が返されるまで待機するようにしている
  • [authors]や[industryIdentifiers]のような複数データを持つ項目は、カンマ区切りの値として出力している
interface ApiJsonBook {
    totalItems:number;
    items: [
      {
        volumeInfo:{
          title:string;       //書籍名
          authors?:string[];  //著者
          publisher:string;   //出版社
          publishedDate:Date; //出版日
          description?:string;//説明
          industryIdentifiers?: [//ISBNコード
            {
              type: string;
              identifier:string;
            }
          ]
        }
      }
    ]
    error?: {
      code: string;
      message: string;
    };
}

async function main(workbook: ExcelScript.Workbook) {

  try{

    // 出力先ワークシートの設定 、データ初期化
    let ws = workbook.getWorksheet("Book_Search_Output");
    if (ws.getUsedRange() != null) {
        ws.getUsedRange().clear();
    }

    //検索キーワードの取得
    let keyword = workbook.getWorksheet("Book_Search_Input").getRange("A1").getValue();

    //定数および最初から使う変数の定義
    const keys = ["title", "authors", "publisher", "publishedDate", "description", "industryIdentifiers"];
    const num_per_page = 40; //APIで読み込める最大件数(超えたらページ送り)
    let max_page = 1; //ページ数の初期値
    let cnt_row_xlsx = 0; //出力シートの行数カウンター
    let url = "https://www.googleapis.com/books/v1/volumes?q=intitle:" + 
      keyword + "&maxResults=" + num_per_page.toString(); //APIのurlの初期値

    for (let cnt_page = 0; cnt_page < max_page; cnt_page++) {

      //2ページ目以降の場合、urlにページ送り用のインデックスを追加
      if (max_page > 1) {
        url = "https://www.googleapis.com/books/v1/volumes?q=intitle:" +
          keyword + "&maxResults=" + num_per_page.toString() + 
            "&startIndex=" + (cnt_page * num_per_page).toString();
      }

      //データ取得の実施
      let res = await fetch(url);
      let apiJson: ApiJsonBook = await res.json();

      //初回ページの場合、全件数からページ数を算出
      if (cnt_page === 0) {
        if (apiJson.totalItems > num_per_page) {
          max_page = Math.floor(apiJson.totalItems / num_per_page);
        }
      }

      //全件数がゼロの場合、メッセージを出力して終了
      if (apiJson.totalItems == 0) {
        console.log("該当データがありませんでした。");
        return;
      }

      // 取得したデータのシート書き出し
      for (let cnt_row = 0; cnt_row < apiJson.items.length; cnt_row++) {
        for (let cnt_col = 0; cnt_col < keys.length; cnt_col++) {

          //タイトル行
          if (cnt_row === 0 && cnt_page === 0) {//タイトル行
            ws.getCell(cnt_row_xlsx, cnt_col).setValue(keys[cnt_col]);
          }

          //データ行(authors列)
          if (keys[cnt_col] === "authors") {
            let tmp_array = "";
            if (apiJson.items[cnt_row].volumeInfo[keys[cnt_col]]) {//値があれば書き出し
              for (let cnt_array = 0; cnt_array < apiJson.items[cnt_row].volumeInfo[keys[cnt_col]].length; cnt_array++) {
                if (cnt_array > 0) {
                  tmp_array = tmp_array + ",";
                }
                tmp_array = tmp_array + apiJson.items[cnt_row].volumeInfo[keys[cnt_col]][cnt_array];
              }
              ws.getCell(cnt_row_xlsx + 1, cnt_col).setValue("'" + tmp_array);
            }

          //データ行(industryIdentifiers)
          } else if (keys[cnt_col] === "industryIdentifiers") {
            let tmp_array = "";
            if (apiJson.items[cnt_row].volumeInfo[keys[cnt_col]]) {//値があれば書き出し
              for (let cnt_array = 0; cnt_array < apiJson.items[cnt_row].volumeInfo[keys[cnt_col]].length; cnt_array++) {
                if (cnt_array > 0) {
                  tmp_array = tmp_array + ",";
                }
                tmp_array = tmp_array + apiJson.items[cnt_row].volumeInfo[keys[cnt_col]][cnt_array]["identifier"];
              }
              ws.getCell(cnt_row_xlsx + 1, cnt_col).setValue("'" + tmp_array);
            }

          //データ行(その他の列)
          } else {
            if (apiJson.items[cnt_row].volumeInfo[keys[cnt_col]]) { //値があれば書き出し
              ws.getCell(cnt_row_xlsx + 1, cnt_col).setValue
                ("'" + apiJson.items[cnt_row].volumeInfo[keys[cnt_col]]);
            }
          }
        }

        //出力シートの行数カウンターをインクリメント
        cnt_row_xlsx = cnt_row_xlsx + 1;
      }
    }

    //出力完了
    console.log("書籍データの出力が正常終了しました。");

  } catch (error) {
    console.log("書籍データの出力が異常終了しました。", error);
  }
}

 

動作確認する

スクリプトの実行ボタンを押し、処理終了のメッセージが表示されたら、[Book_search_Output]シートを開いて結果を確認します。

 

下スクロールして全件出力されていることを確認します。

 

おわりに

とてもシンプルな例ですが、Office ScriptでAPI呼出しを行う基本形がつかめるかと思います。Office Scriptで初めてAPI呼出しを行う際の参考になれば幸いです。

※Office ScriptでのAPI呼出しのサポートに関する公式記事:Office スクリプトでの外部 API 呼び出しのサポート - Office Scripts | Microsoft Learn