【GAS】スプレッドシートに入力したキーワードで検索してヒットした画像をGoogleドライブに保存する

2626 語
13 分
【GAS】スプレッドシートに入力したキーワードで検索してヒットした画像をGoogleドライブに保存する

はじまり#

リサちゃん avatar
リサちゃん
これ1個ずつ検索してダウンロードするの面倒くさいなあ・・・
135ml avatar
135ml
300個あるもんなあ
リサちゃん avatar
リサちゃん
はああ・・・
135ml avatar
135ml
ちょっとでも作業を減らせれば良いなあ。

概要#

今回は、 Google Apps Script (GAS)を使って、 Google のサービスを自動化していきます。

実装する処理の目的は、アプリのロゴをなるべく自動で集めて楽をすることです。

そこで、 GAS を使用してスプレッドシートに入力されたキーワードに基づいて画像を検索し、見つかった最初の画像を Google ドライブに自動的に保存する処理を作っていきます。 どれぐらい行けそうなものか試してみましょう。

こんな風に入力して、

こんな風に画像が保存されることを期待します。

前準備#

スクリプトを書く前に、諸々と準備が必要です。

  • Custom Search API を有効にする。
  • API キーを入手する。
  • カスタム検索エンジン(Custom Search Engine)を設定する。

Custom Search APIを有効にする#

まずは、 Google Cloud Platform のコンソール画面で、「Custom Search API」というものを有効にします。

「APIとサービス」>「ライブラリ」と遷移して、検索します。

そして、「Custom Search API」が有効になっていることを確認します。

APIキーを入手する#

次に、この Custom Search API を利用するためのAPIキーを入手しましょう。

「APIとサービス」>「認証情報」と遷移した先の画面で、「認証情報を作成」をクリックして、「APIキー」を選択します。

API キーが作成されたら編集します。 デフォルトでは、プロジェクトで有効にしている全ての API にアクセスできるようになっていると思うので、 1 つの API キーで利用できる API を一つに限定させます。

「APIの制限」の部分で、「Custom Search API」だけを選択して保存します。

これで、この API キーは Custom Search API しか利用できないキーになりました。セキュリティセキュリティ。 「API Key」にある値は、後で使います。

カスタム検索エンジン(Custom Search Engine)を設定する#

そして、この準備も必要です。

「カスタム検索エンジン」というものを作ることで、スクリプトで実行する検索方法を設定します。

まずは、以下のサイトに飛んで、「使ってみる」を選びます。

そして、カスタム検索エンジンを作ります。

ここで忘れてはならないのが、「検索設定」の部分で「画像検索」を ON にすることです!

ちゃんと「画像検索」が ON になっているカスタム検索エンジンが作成できたら、そのエンジンの「検索エンジンID」をメモります。後で使います。

スプレッドシートの準備#

そうしたら、事前準備は終わったので、 GAS を実装していきましょう。

今回は、スプレッドシート上に入力したキーワードで検索するので、スプレッドシート上に入力部を準備します。

こんな感じの入力部分を作ります。今回は 2 列分作ります。

そして、冒頭のようにキーワードを並べていきます。 右側には、保存する画像ファイルの名前を入力します。拡張子は除いた形です。

スクリプトの実装#

そして、やっとスクリプトを実装していきます。

処理の概要#

まずは、処理の本流になります。

/**
* @description Check whether the element is an empty value.
* @param {any} element
* @return {boolean}
*/
function checkEmpty(element) {
return element !== undefined && element !== 0 && element !== null && element !== "";
}
/**
* @description Check whether empty element is existing.
* @param {any[]} array
* @return {boolean}
*/
function isEmptyExisting(array) {
let empties = array.filter(element => !checkEmpty(element));
if(empties.length > 0){
return true;
}
return false;
}
/**
* @description Search by keywords specified in the sheet and save into the Google Drive.
* @param {boolean} isRecording
* @return {boolean}
*/
function searchToSaveImageToDrive(isRecording=true) {
const folderIdToSaveLogoBySearching = FOLDER_ID_TO_SAVE_LOGO_BY_SEARCHING;
const sheetName = SHEET_NAME_6TH;
const startRow = ROW_INDEX_5TH_TO_START_OUTPUTING_RECORDS;
const startColumn = COLUMN_INDEX_5TH_OF_FILE_LIST_TO_SAVE_IMAGE_BY_SEARCH;
console.log(`searchToSaveImageToDrive: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`);
let sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
let records = sheet.getRange(startRow, startColumn, sheet.getLastRow() - startRow + 1, 2).getValues();
records = records.filter(values => !isEmptyExisting(values));
console.log(records);
console.log(`searchToSaveImageToDrive: cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc`);
let gottenBlobs = records.map(rec => {
return searchByKeywordToGetImages(rec[0], rec[1]);
});
console.log(gottenBlobs);
console.log(`searchToSaveImageToDrive: dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd`);
let saveImageFiles = gottenBlobs.map(blobInfo => {
return createImageFilesFromBlobs(blobInfo, folderIdToSaveLogoBySearching);
});
saveImageFiles = saveImageFiles.filter(value => checkEmpty(value));
console.log(saveImageFiles);
console.log(`searchToSaveImageToDrive: ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg`);
const myPromise = new Promise((resolve, reject) => {
recordLog(MY_SCRIPT_NAME, "searchToSaveImageToDrive", isRecording);
});
console.log(`searchToSaveImageToDrive: iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii`);
let folderUrl = `https://drive.google.com/drive/folders/${folderIdToSaveLogoBySearching}`;
displayHtml("index_html", "Saving Files By Searching Terminated...", `<h2>Done.</h2><p>${saveImageFiles.length} images saved in ${getHref(folderUrl, "this folder")}.</p>`);
console.log(`searchToSaveImageToDrive: kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk`);
return true;
}

処理流れとしては、以下の2部構成です。

  • 画像検索の処理
  • 画像を取得して Google ドライブに保存する処理

画像検索の処理#

まずは、gottenBlobsという配列に画像検索してヒットした画像の Blob オブジェクトを含む情報を格納していきます。

この行の処理で、スプレッドシートからキーワードとファイル名を取得します。

let records = sheet.getRange(startRow, startColumn, sheet.getLastRow() - startRow + 1, 2).getValues();

また、checkEmpty()isEmptyExisting()という関数は、null""(空文字)になっている要素を省くために使います。

/**
* @description Check whether the element is an empty value.
* @param {any} element
* @return {boolean}
*/
function checkEmpty(element) {
return element !== undefined && element !== 0 && element !== null && element !== "";
}
/**
* @description Check whether empty element is existing.
* @param {any[]} array
* @return {boolean}
*/
function isEmptyExisting(array) {
let empties = array.filter(element => !checkEmpty(element));
if(empties.length > 0){
return true;
}
return false;
}

そしたら、この処理で Blob オブジェクトを含んだオブジェクトの配列を取得します。

let gottenBlobs = records.map(rec => {
return searchByKeywordToGetImages(rec[0], rec[1]);
});

まずは、searchByKeywordToGetImages()の処理はこんな感じです。

/**
* @description Search by specified keyword to get blob object.
* @param {string} keyword
* @param {string} gettingBlobName
* @return {any{}}
*/
function searchByKeywordToGetImages(keyword, gettingBlobName){
const apiKey = API_KEY_TO_SEARCH_BY_CUSTOM_SEARCH_API;
const searchEngineId = SEARCH_ENGINE_ID_TO_SAVE_IMAGE;
let blob, extension, blobFullName;
let keywordToSearch = keyword.replaceAll(" ", "+").replaceAll(" ", "+");
keywordToSearch = `${encodeURIComponent(keywordToSearch)}+logo`;
let searchUrl = `https://www.googleapis.com/customsearch/v1?q=${keywordToSearch}&cx=${searchEngineId}&searchType=image&key=${apiKey}&num=1`;
let response;
try{
response = UrlFetchApp.fetch(searchUrl);
}catch(error){
return {"blob": null, "name": null};
}
let results = JSON.parse(response.getContentText());
console.log(results);
console.log(`searchByKeywordToGetImages: eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`);
if (results.items && results.items.length > 0) {
let imageUrl = results.items[0].link;
let imageResponse;
try{
imageResponse = UrlFetchApp.fetch(imageUrl);
}catch(error){
return {"blob": null, "name": null};
}
blob = imageResponse.getBlob();
console.log(blob);
console.log(`searchByKeywordToGetImages: gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg`);
// 元の画像のURLからファイルの拡張子を取得します
extension = imageUrl.substring(imageUrl.lastIndexOf('.'));
// 拡張子がある場合、クエリパラメーターまたは他のパスの一部を含まないようにします
if (extension.indexOf('?') !== -1) {
extension = extension.substring(0, extension.indexOf('?'));
}
// 拡張子らしき文字列が6文字以上もしくは拡張子が正しく取得できない場合は、デフォルトとして.jpgを使用
if (extension.length >= 6 || extension.indexOf('/') !== -1) {
extension = '.jpg';
}
blobFullName = `${gettingBlobName}${extension}`
console.log(blobFullName);
console.log(`searchByKeywordToGetImages: iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii`);
} else {
console.log('No images found for: ' + keywordToSearch);
console.log(`searchByKeywordToGetImages: jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj`);
}
console.log(`searchByKeywordToGetImages: kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk`);
return {"blob": blob, "name": blobFullName};
}

画像検索をする流れは以下です。

  1. 半角スペースと全角スペースを考慮した検索文字列にする。
  2. その検索文字列にlogoというキーワードも追加して、ロゴが画像検索にヒットしやすくする。
  3. 検索用 URL が出来たら、UrlFetchApp.fetch()を実行する。
  4. 検索結果から、最初にヒットした画像がある URL を取得する。
  5. 再び、UrlFetchApp.fetch()を実行して、画像のデータを取得する。
  6. 取得した画像にファイル名を付ける。
  7. Blobオブジェクトファイル名を返す。

特に、「取得した画像にファイル名を付ける。」処理ですが、その処理は以下の流れです。

  1. ここではまず、元々の画像の拡張子を取得します。
  2. しかし、 URL から取得した拡張子にはクエリパラメータが含まれている可能性があるので、クエリパラメーターを削ります。
  3. そして、クエリパラメーターが削られたら、.jpegとか.pngとか.webpといった拡張子が来ることを期待しますが、ファイル名に.が含まれていてちゃんと取得できない可能性があります。
  4. そこで、拡張子が 6 文字以上だったら、.jpgで拡張子を決定します。(画像フォーマットだったら大体このくらいの長さでいいんじゃないでしょうか。)

.を考慮しないと、こんな画像ファイルが生まれてきてしまうので、

.の長さを考慮することにします。

これで、画像を保存する下地は出来ました。

画像を取得してGoogleドライブに保存する処理#

画像オブジェクトを Google ドライブに保存します。

/**
* @description Create image files into the Google Drive.
* @param {any{}} blobInfo
* @param {string} folderId
* @return {DriveApp.File}
*/
function createImageFilesFromBlobs(blobInfo, folderId=FOLDER_ID_TO_SAVE_LOGO_BY_SEARCHING){
let folder = DriveApp.getFolderById(folderId);
console.log(`createImageFilesFromBlobs: eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`);
if(Object.prototype.toString.call(blobInfo["name"]) === "[object Null]"){
return null;
}
let fileName = blobInfo["name"];
let file = DriveApp.createFile(blobInfo["blob"].setName(fileName));
file.moveTo(folder);
console.log(`createImageFilesFromBlobs: iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii`);
console.log('Image saved to Drive with name: ' + fileName);
console.log(`createImageFilesFromBlobs: kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk`);
return file;
}

ここではもう、渡されたオブジェクトをファイルオブジェクトのプロパティに割り当てているだけになります。

これで、画像が目当ての Google ドライブフォルダの中に保存されました!

処理の本流にあるdisplayHtml()という処理では、何個のファイルが保存されたかを表示しています。

displayHtml("index_html", "Saving Files By Searching Terminated...", `<h2>Done.</h2><p>${saveImageFiles.length} images saved in ${getHref(folderUrl, "this folder")}.</p>`);

じゃあ、どれぐらいロゴが保存できるんだろう?#

さて、処理が出来たところで、この画像検索による処理の精度が気になってきます。

Google ドライブで画像を一覧で眺めてみると・・・、

ふむふむ、なるほど・・・

まあ、画像検索のロゴを取得する精度としては、 50 ~ 60 点というところでしょうか。

個人的には、以下の点がまだ至っていないなあと思いました。

  • 文字が入っていないロゴが欲しい。
  • 少しマイナーなサービスだと厳しいかなあ。
  • なんか画像じゃないファイルが混ざってる・・・(これは、画像検索処理で結果を複数取得して、先頭から画像かどうかで処理を分岐させれば回避できそうですね。)
  • あと、画像の取得処理が遅い!(非同期にすれば全然速くなりそうです。)

まとめ#

これで、スプレッドシートに入力されたキーワードで画像を検索し、ヒットした最初の画像を Google ドライブに自動で保存する機能が一旦完成しました。

本記事は、以下の流れで書かれました。

  1. Custom Search API の有効化
  2. API キーの取得
  3. カスタム検索エンジンの設定
  4. Google スプレッドシートの準備
  5. GAS スクリプトの実装(画像検索して画像を取得)
  6. GAS スクリプトの実装(取得した画像を Google ドライブに保存)

Google のポリシーと利用規約や、 API の使用制限、クォータなどにも気を付けて使ってみましょう。保存される画像の使用範囲や著作権にも注意ですね。(ちなみに、 Custom Search API は、1 日 100 リクエストより多くリクエストすると有料になります。)

おしまい#

リサちゃん avatar
リサちゃん
まあ、作業が半分以上は減ったと思うと悪くないかなあ
135ml avatar
135ml
手頃に作りました。

以上になります!

記事を共有

この記事が役に立ったなら、ぜひ他の人と共有してください!

【GAS】スプレッドシートに入力したキーワードで検索してヒットした画像をGoogleドライブに保存する
https://endorphinbath.com/posts/gas-search-to-save-images/
著者
kinkinbeer135ml
公開日
2024-03-09
ライセンス
CC BY-NC-SA 4.0
関連記事 スマート
1
【GAS、Google Spreadsheet】ブログに使用した画像をGoogleドライブで管理するために書いたスクリプト
Code 僕は、既に公開した記事で使用したファイルをGoogleドライブの所定のフォルダに保存しています。その画像を完了済みのフォルダに定期的に移動してくれるスクリプトを作りました。
2
【GAS、Google Spreadsheet】Googleドライブのフォルダに有るファイルを一覧で取得するスクリプトです
Code GoogleDriveの指定のフォルダにあるファイルを一覧で取得します。Googleドライブを整理したい時に役立つツールになるかと思います。
3
【GAS、Google Spreadsheet】Googleドライブのルートフォルダに有るフォルダを一覧で取得するスクリプトです
Code GoogleDriveのルートフォルダにあるフォルダを一覧で取得します。Googleドライブを整理したい時に役立つツールになるかと思います。
4
【GAS、Google Spreadsheet】Googleドライブ内の指定したフォルダに所定のファイルをコピーするスクリプトです
Code GoogleDriveの指定のフォルダにファイルをコピーするツールを作りました。何かWebの記事をGoogleドキュメントにクリップしたり、指定のフォルダにメモ書きやシートを作成したい場合に役立つかと思います。
5
【GAS、Google Spreadsheet】ドライブにある画像をセルに表示しまくるツール
Code 僕は、ブラウザで言うところの「お気に入り」をGoogleスプレッドシートで管理しているのですが、その一覧の可視性を上げるためにサイトのアイコンをGoogleドライブから一気に設定するツールを作りました。
ランダム記事 ランダム
Profile Image of the Author
kinkinbeer135ml
SIerをやめて、プログラミングを勉強しています。※Amazonアソシエイトに参加しています。
お知らせ
私のブログへようこそ!これはサンプルのお知らせです。
音楽
カバー

音楽

再生中なし

0:00 0:00
歌詞なし
カテゴリ
タグ
サイト統計
記事
287
カテゴリー
8
タグ
93
総文字数
486,174
運用日数
0
最終活動
0 日前

目次