記事内に広告が含まれています。
スポンサーリンク

GASとGemini APIでブログの分析・記事下書き・リライトを自動化した話(詰まったところ全部書く)

AI
この記事は約10分で読めます。

10年で130記事ほど書いてきたこのブログですが、正直なところ更新のペースは年々落ちていました。書くこと自体は苦ではないのに、「何を書くか決める」「アクセス解析を眺めて改善点を探す」といった前後の作業が面倒で、結局手が止まる。よくある話だと思います。

そこで、Google Apps Script(GAS)とGemini APIを使って、アクセス解析から記事の下書き生成、既存記事のリライト提案までを半自動化する仕組みを作ってみました。

この記事は自慢話ではなく、セットアップで詰まった箇所とそのエラーメッセージを全部並べた備忘録です。同じことをやろうとして詰まった人の役に立てばと思います。

スポンサーリンク

作ったものの全体像

3つのジョブに分けて、GASの時間トリガーで動かしています。

ジョブA(週1回・月曜朝) GA4のデータとSearch Consoleのデータを取得して、Gemini APIに分析させます。出力は「今週の総括」「伸びた/落ちたページ」「リライト候補」「新規記事のテーマ候補」。分析レポートはGoogleドキュメントに追記され、テーマ候補はスプレッドシートに「未承認」の状態で並びます。

ジョブB(毎朝) スプレッドシートで自分が「承認」にした行だけを拾って、記事本文とアイキャッチ画像を生成し、WordPressに下書きとして投稿します。公開は必ず手動です。

ジョブC(四半期) 過去12ヶ月分のデータでサイト全体を評価し、リライト候補を別タブに書き出します。こちらも提案までで、記事の書き換えは承認した行だけを別関数で実行します。

設計上こだわったのは次の2点です。

  • 承認制にする:AIが勝手にテーマを決めて記事を量産する形にはしませんでした。提案はAI、採用の判断は人間。
  • 公開は絶対に自動化しない:WordPressへの投稿は status=draft 固定で、公開処理そのものを実装していません。

AIの下書きは、正直に言えば「70点の無難な記事」です。無難な記事は今の検索では埋もれます。だから公開前に自分の経験や具体例を足す前提で、記事内に [要確認] というマーカーを入れさせる設計にしました。

スポンサーリンク

費用は月200〜500円ほど

無料版のGeminiアプリを使う案も検討しましたが、GASから叩くのは「Gemini API」で、GeminiアプリやAI Plus/Proといったサブスクとは課金経路が完全に別物でした。サブスクを契約してもAPIの枠は1リクエストも増えません。ここは最初に勘違いしていた点です。

結果として、API側だけを従量課金にしました。実測では記事1本あたり20円ほど(本文+アイキャッチ画像)。週に数本のペースなら月200〜500円程度で収まる計算です。サーバー代とドメイン代に比べれば誤差の範囲でした。

なお現在のGemini APIは、新しく課金設定をする場合プリペイド(前払い)方式になっていました。日本だと最低チャージ額は2,000円。残高がゼロになった時点でリクエストが止まるので、意図しない高額請求が構造的に起きないという意味では、むしろ安心できる仕組みです。

ちなみに画像生成については、Geminiアプリの無料版や個人向け有料プランでは画像の隅にウォーターマークが入りますが、API経由で生成した画像には可視のウォーターマークは入りません。ただしSynthIDという不可視の電子透かしは全ての経路で埋め込まれます。見た目には影響せず、商用利用の妨げにもなりません。


ここから本題の、詰まった箇所の記録です。

スポンサーリンク

詰まりどころ1:Search Console APIが403を返す

最初の接続テストで出たのがこれ。

Google Search Console API has not been used in project 1002024412304
before or it is disabled.

Search Consoleの閲覧権限は付与済みだったので権限の問題かと思いましたが、原因は別で「GCPプロジェクト側でSearch Console APIそのものが有効化されていない」でした。

GA4のデータ取得はGASのAdvanced Serviceを使う方式なのでGCP側の設定が不要ですが、Search Consoleは直接APIを叩くため、APIを明示的に有効化する必要があります。ここで気づいたのが次の問題です。

詰まりどころ2:GASが「触れないGCPプロジェクト」を使っていた

エラーに出ているプロジェクト番号(1002024412304)をGCPコンソールで開こうとすると、こう言われます。

追加のアクセス権が必要です resourcemanager.projects.get(権限がありません)

自分のアカウントで作ったプロジェクトなのに権限がない。混乱しましたが、これはGASプロジェクトを作ると裏側でGoogle管理の隠れGCPプロジェクトが自動生成される仕様のためでした。自動生成されたプロジェクトは、コンソールから直接触れません。

つまり「APIを有効化したいのに、その対象プロジェクトに入れない」という手詰まりです。

解決策は、GASプロジェクトを自分で作った通常のGCPプロジェクトに紐づけ直すこと。手順はこうなります。

  1. GCPコンソールで対象プロジェクトを選び、Search Console APIとGoogle Analytics Data APIを有効化する
  2. そのプロジェクトの「プロジェクト番号」をコピーする
  3. GASエディタ →「プロジェクトの設定」→「Google Cloud Platform(GCP)プロジェクト」→「プロジェクトを変更」に番号を貼る

ちなみに私はここで、Gemini APIキーを作った時に自動でできていた Default Gemini Project をそのまま使いました。結果的にこれが正解でした(理由は後述)。

詰まりどころ3:同意画面がないと紐づけできない

上記3の手順で番号を入れると、今度は「OAuth同意画面を設定してください」と怒られます。

GCPプロジェクトにOAuth同意画面が用意されていないと、GASを紐づけられない仕様です。GCPコンソールで「OAuth同意画面」を開いて、アプリ名・サポートメール・対象(外部)・連絡先メールを入力して作成すれば通ります。

なお、この画面にある「OAuthクライアントを作成」ボタンは今回の用途では不要です(押さなくてよい)。

詰まりどころ4:access_denied(403)でブロックされる

紐づけ後にスクリプトを実行すると、今度はこう出ました。

アクセスをブロック: site-analyzer は Google の審査プロセスを完了していません エラー 403: access_denied

新しく作った同意画面が「テスト中」ステータスで、自分がテストユーザーとして登録されていないためです。OAuth同意画面の「対象(Audience)」にあるテストユーザー欄に自分のメールアドレスを追加すれば解決します。

個人利用のツールなら、この「テスト中」のままで問題なく動き続けます。審査に出す必要はありません。

詰まりどころ5:Search Consoleのプロパティ形式の不一致

API有効化が済んだあと、今度はこのエラー。

User does not have sufficient permission for site 'https://yukibowz.net'.

設定値に https://yukibowz.net を入れていたのが原因でした。私のSearch Consoleは「ドメインプロパティ」と「httpsのURLプレフィックス」の2種類で登録していて、http:// で始まるプロパティは存在していなかったのです。

Search Console APIで指定するサイトURLは、登録形式と完全に一致させる必要があります

  • ドメインプロパティの場合:sc-domain:example.com(httpsやスラッシュは付けない)
  • URLプレフィックスの場合:https://example.com/(末尾スラッシュまで含めて登録どおり)

http/https/wwwの有無をまとめて見られるので、ドメインプロパティ(sc-domain:)を指定するのが結果的に一番きれいでした。

詰まりどころ6:メール通知でスコープ不足

データ収集と分析は成功し、ログにも topPages=25, queries=100 と出たのに、最後のメール通知で落ちました。

Specified permissions are not sufficient to call Session.getEffectiveUser.
Required permissions: https://www.googleapis.com/auth/userinfo.email

通知先アドレスを取得する処理に必要なスコープが appsscript.json に入っていなかためです。oauthScopeshttps://www.googleapis.com/auth/userinfo.email を追加すれば解決します。

スコープを追加すると再承認が求められるので、もう一度OAuthの許可を通す必要があります。

詰まりどころ7:画像生成のモデル名とMIMEタイプ

画像生成まわりは仕様変更が激しく、ここでも2回つまずきました。

まずモデル名。古い記述のまま gemini-2.5-flash-image を指定していたので、現行の gemini-3.1-flash-image(Nano Banana 2)に差し替えました。プレビュー版のIDは停止済みなので、-preview が付かないGA版を指定する必要があります。

次にこのエラー。

The value 'image/png' is not supported for 'response_format.mime_type'.
Supported values: 'image/jpeg'.

メッセージが答えを言ってくれています。PNGは非対応で、JPEGのみ。ここを直すときは、WordPressへのメディアアップロード側のContent-Typeとファイル名の拡張子も合わせて変更するのを忘れないでください。生成はJPEGなのに「PNGです」というラベルで送ると整合が取れません。

詰まりどころ8(最大の落とし穴):APIキーのプロジェクト違い

一番時間を取られたのがこれです。テキスト生成は正常に動くのに、画像生成だけがひたすら429を返す。

You exceeded your current quota, please check your plan and billing details.

課金設定は済んでいるし、残高もあるし、Gemini APIも有効。それでも429。

原因は、Gemini APIキーを作ったプロジェクトと、課金(チャージ)したプロジェクトが別だったことでした。私は最初に「ブログ用」として新しいGCPプロジェクトを作ってそこでAPIキーを発行し、その後の流れでチャージは Default Gemini Project 側に入っていたのです。

課金の判定は「GASが紐づいているプロジェクト」ではなく「APIキーが属しているプロジェクト」で行われます。つまりリクエストは残高のないプロジェクト扱いになり、無料枠で処理されていました。

そしてここが厄介なのですが、テキスト生成は無料枠が大きいので普通に成功してしまいます。一方で画像生成は無料枠がほぼないため、真っ先に429になる。「テキストは通るのに画像だけ落ちる」という症状は、この課金経路のねじれを疑うと早いです。

解決は、課金しているプロジェクトで新しいAPIキーを発行して差し替えるだけ。それだけで通りました。

詰まりどころ9:レンタルサーバーのWAFに弾かれる

最後の関門はサーバー側でした。WordPress REST APIへのPOSTがWAF(Web Application Firewall)に攻撃と誤認され、投稿がブロックされます。

WAFを一時的にオフにすれば動くのですが、それは根本対応ではありません。正しくはブロックログから該当するルールを特定して、その項目だけ除外設定するという手順になります。ここは現在進行中です。

やってみて分かったこと

技術的な結論としては、GAS+Gemini API+WordPress REST APIの組み合わせは想像以上に相性がいいというのが率直な感想です。サーバーを立てる必要もなく、月数百円で回ります。

一方で、詰まりどころのほとんどはAIやコードではなく、GoogleアカウントとGCPプロジェクトの権限・課金まわりでした。振り返ると9つのうち6つがそこです。同じことをやる人は、最初に次の3点を紙に書いて整えてから始めると、かなりの時間を節約できると思います。

  1. GASを実行するアカウントはどれか
  2. GA4とSearch Consoleの権限は、そのアカウントに渡っているか
  3. APIキーは、課金設定をしたプロジェクトで発行したものか

そして運用面での本音を書いておくと、この仕組みは「記事を勝手に増やしてくれる魔法」ではありません。テーマを承認し、下書きに自分の経験を足し、[要確認] を検証する手間は残ります。むしろ残すべき手間はどこかを決めるための仕組みだと考えています。

効果が見えるのは早くても3〜6ヶ月後でしょう。そのときの数字も、また記事にする予定です。

コメント

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