Claude Media
Claude Codeで入稿規定のバナーサイズをチェックする手順

Claude Codeで入稿規定のバナーサイズをチェックする手順

Google広告の静止画・GIFバナーの規定を表にして、フォルダ内のファイルを一括検査するスクリプトをClaude Codeに書かせる手順です。形式・サイズ・容量・GIFの尺とFPSを見て、不適合の一覧を出します。

入稿直前に「1枚だけサイズが1ピクセルずれていた」「容量が数KB超えて差し戻された」と気づくのは、バナー運用でよくある手戻りです。規定を表にして検査スクリプトを作っておけば、フォルダを渡すだけで不適合の一覧が出ます。そのスクリプトの下書きはClaude Codeに書かせられます。

この記事では、Google広告のディスプレイ広告に入稿する静止画とGIFを例に、規定の表(JSON)、検査スクリプト、実行結果の読み方、そして繰り返し回すための設定までを順に説明します。動画ファイルは、尺と容量をffprobeで取って同じ表に当てる形です。

規定を人が表にしてから、スクリプトを書かせる

Claude Codeに「入稿規定をチェックして」とだけ頼むと、規定の数字をClaudeの記憶から補うことになりかねません。数字は人が一次ソースから拾い、ファイルにして渡します。スクリプトの仕事は、その表に対して実ファイルを照合するところだけに絞ります。

Google広告のヘルプにある、アップロード型ディスプレイ広告(アニメーションあり・なしの画像広告)の規定は次のとおりです。

数字

静止画・GIFバナーの主な規定

  • ファイル形式

    GIF / JPG / PNG

    アニメーション・静止画とも

  • 最大容量

    150KB

    1ファイルあたり

  • GIFの長さ

    30秒以内

    ループ可。ただし30秒で停止する

  • GIFのFPS

    5未満

    5FPSより遅いこと

Google広告ヘルプ「Uploaded display ads specifications」の画像広告の表より

サイズは次の表にあるピクセル寸法のいずれかに一致させます。ページには、地域によって表示される広告サイズが限られるという注記もあります。

区分サイズ(幅×高さ)
正方形・長方形サイズ(幅×高さ)200×200、240×400、250×250、250×360、300×250、336×280、580×400
スカイスクレイパーサイズ(幅×高さ)120×600、160×600、300×600、300×1050
リーダーボードサイズ(幅×高さ)468×60、728×90、930×180、970×90、970×250、980×120
モバイルサイズ(幅×高さ)300×50、320×50、320×100

この数字を、スクリプトが読むJSONにします。複数の媒体を扱うなら、キーを媒体名にして規定を並べます。

{
  "gdn-image": {
    "source": "https://support.google.com/google-ads/answer/1722096",
    "formats": ["GIF", "JPEG", "PNG"],
    "max_kb": 150,
    "sizes": ["300x250", "336x280", "728x90", "970x250",
              "160x600", "300x600", "320x50", "320x100"],
    "gif_max_seconds": 30,
    "gif_max_fps": 5
  }
}

ここでは例として、使うサイズだけを抜き出しています。表のすべてを許可するか、自社で使う分だけに絞るかは運用次第です。規定は更新されるので、JSONには出典URLを残しておくと、後から見直す人が元のページに戻れます。

検査スクリプトをClaude Codeに書かせる

表ができたら、Claude Codeに次の形で依頼します。ポイントは、検査項目と出力の形、そして「読めないファイルも不適合として出す」という失敗時の扱いまで、指示に含めることです。

banners/ 以下の画像を specs.json の "gdn-image" の規定で検査する
Pythonスクリプト check_creatives.py を作ってください。
- 画像の読み取りには Pillow を使う
- 検査項目: 形式、幅x高さ、容量(KB)、GIFの尺と平均FPS
- 拡張子で対象を絞らず、フォルダ内の全ファイルを見る
  (規定外の形式や壊れたファイルも不適合として出す)
- 不適合は「NG パス: 理由 / 理由」の1行で出力し、最後に件数を出す
- 不適合があれば終了コード1で終わる
- 規定の数字はスクリプトに書かず specs.json から読む

Claude Codeは自分でファイルを書き、実行までします。出来上がるスクリプトの一例は次のとおりです。PillowのImage.openで形式とサイズを読み、GIFだけフレームごとの表示時間を足して尺を出します。

#!/usr/bin/env python3
"""banners/ 以下の画像を specs.json の規定で検査し、不適合を一覧にする。"""
import json, sys
from pathlib import Path
from PIL import Image
 
def check(path, spec):
    problems = []
    kb = path.stat().st_size / 1024
    if kb > spec["max_kb"]:
        problems.append(f"容量超過 {kb:.0f}KB > {spec['max_kb']}KB")
    try:
        with Image.open(path) as im:
            fmt, size = im.format, f"{im.width}x{im.height}"
            if fmt not in spec["formats"]:
                problems.append(f"形式 {fmt} は対象外")
            if size not in spec["sizes"]:
                problems.append(f"サイズ {size} は規定外")
            if fmt == "GIF" and getattr(im, "n_frames", 1) > 1:
                total_ms = 0
                for i in range(im.n_frames):
                    im.seek(i)
                    total_ms += im.info.get("duration", 100)
                sec = total_ms / 1000
                fps = im.n_frames / sec if sec else 0
                if sec > spec["gif_max_seconds"]:
                    problems.append(f"GIFの尺 {sec:.1f}秒 > {spec['gif_max_seconds']}秒")
                if fps >= spec["gif_max_fps"]:
                    problems.append(f"GIFのFPS {fps:.1f} は {spec['gif_max_fps']} 未満が条件")
    except OSError as e:
        problems.append(f"画像として開けない: {e}")
    return problems
 
def main(folder, spec_name):
    spec = json.load(open("specs.json"))[spec_name]
    ng = 0
    for p in sorted(Path(folder).rglob("*")):
        if not p.is_file() or p.name.startswith("."):
            continue
        probs = check(p, spec)
        if probs:
            ng += 1
            print(f"NG {p}: " + " / ".join(probs))
    print(f"不適合 {ng} 件")
    sys.exit(1 if ng else 0)
 
if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2])

実行は、フォルダと規定名を渡すだけです。

python3 check_creatives.py banners gdn-image

形式違い・1ピクセルずれ・容量超過・GIFの速すぎを1枚ずつ混ぜたテスト用フォルダ(Pillow 12.3.0)で動かすと、出力は次のようになりました。

NG banners/anim.gif: GIFのFPS 8.2 は 5 未満が条件
NG banners/bad.webp: 形式 WEBP は対象外
NG banners/bad_300x251.png: サイズ 300x251 は規定外
NG banners/big_970x250.png: 容量超過 496KB > 150KB
不適合 4 件

規定に合う300×250のPNGは出力に現れません。終了コードは不適合があれば1になるので、シェルスクリプトやCIから呼ぶときも使えます。

検査が素通りしやすい箇所を、指示で先に塞ぐ

検査スクリプトの弱点は、見ていないものを「問題なし」として黙って通すことです。次の4点は、依頼文かレビューで押さえます。

落とし穴

素通りを生む4つの穴

  • 拡張子で絞ってしまう

    .pngと.jpgだけを対象にすると、.webpや拡張子の無いファイルが検査から外れます。上の指示で「全ファイルを見る」と書いたのはこのためです。

  • GIFのFPSは概算になる

    フレーム数を尺で割った平均値です。フレームごとに表示時間が違うGIFでは、最速の区間が5FPSを超えていても平均が下回ることがあります。境界の近いGIFは書き出し側で調整します。

  • KBの数え方

    ヘルプの「150KB」が1,000バイトか1,024バイトかは表から読み取れません。スクリプトは1,024で割っているため、150KBぎりぎりのファイルは、広告管理画面での判定が正になります。

  • 地域限定のサイズ

    ヘルプには、一部のサイズが特定の地域でしか配信されないという注記があります。表に載っていても、出稿先で使えるとは限りません。

もう1つ、見ていないものがあります。HTML5広告(ZIP内に1つのHTMLと最大39のメディアファイル、ZIP全体で600KB以下)やAMPHTML広告(<meta name="ad.size" content="width=W,height=H">でサイズを宣言)は、ヘルプでは別の規定です。画像用のspecs.jsonとは別のキーで追加してください。

動画は尺と容量をffprobeで取って同じ表に当てる

動画ファイルの規定は、媒体と配置ごとに違います。上で引いたGoogle広告のヘルプの画像広告の表に動画の数字はないので、ここでは「尺」「容量」の上限を、入稿先の公式ページで確かめた値でspecs.jsonに足す前提にします。取得はffprobeに任せます。

import json, subprocess
 
def video_info(path):
    out = subprocess.run(
        ["ffprobe", "-v", "error", "-show_entries", "format=duration,size",
         "-of", "json", str(path)],
        capture_output=True, text=True, check=True,
    ).stdout
    fmt = json.loads(out)["format"]
    return float(fmt["duration"]), int(fmt["size"]) / 1024  # 秒, KB

check関数の中で拡張子が.mp4などのときだけこちらに分岐させ、max_secondsとmax_kbを規定と比べます。ffmpegまわりの権限設計やffprobeでの検証の考え方は、Claude Codeでffmpegを使う手順にまとめています。ffprobeは読み取り専用の操作なので、許可に回しやすいコマンドです。

毎回の入稿前に回す設定を整える

スクリプトができたら、毎回同じ手順で呼べるようにします。使うのは、CLAUDE.md、権限設定、非対話実行の3つです。

CLAUDE.mdは、Claude Codeがセッションの開始時に読む指示ファイルです。検査の手順を書いておけば、次回以降の依頼が短くなります。

## 入稿前チェック
- 入稿用の画像は `banners/` に置く。規定は `specs.json` が正で、数字をスクリプトに直書きしない
- 検査は `python3 check_creatives.py banners <規定名>` で行い、NGが出たら一覧を貼って原因別に直す
- 規定を足すときは、出典URLと確認日を `specs.json` の `source` に残す

権限は、検査スクリプトの実行だけを自動で許可します。Bashの許可規則はBash(コマンド *)の形で書き、末尾のワイルドカードの前にスペースを置くと、そのコマンドの引数違いだけに一致します。

{
  "permissions": {
    "allow": [
      "Bash(python3 check_creatives.py *)",
      "Bash(ffprobe *)"
    ]
  }
}

この設定を.claude/settings.jsonに置けば、検査の実行のたびに確認が出なくなります。画像の書き換えや削除は許可に含めていないので、直す作業は引き続き確認付きです。

非対話で回すなら、claude -pにプロンプトと--allowedToolsを渡します。たとえば納品フォルダの検査結果を貼って、直し方の案だけを出させる使い方です。

python3 check_creatives.py banners gdn-image > ng.txt
cat ng.txt | claude -p "NGの原因を、再書き出しが必要なものと、圧縮だけで足りるものに分けて" \
  --allowedTools "Read"

検査そのものは決定的なスクリプトに任せ、Claudeには結果の仕分けと直し方の提案だけを頼む分担です。判定をClaudeの目視に任せると、同じ画像が日によって通ったり落ちたりします。

容量超過とサイズずれの直し方も、元ファイルを残して頼む

NGの一覧が出たら、次の作業は直しです。ここでも「元の画像を上書きしない」を指示に入れます。圧縮は不可逆で、品質を落としすぎた画像を元に戻せなくなるためです。

ng.txt のうち「容量超過」だけを対象に、banners/ の画像を
fixed/ へ書き出してください。
- JPEGは quality を85から5ずつ下げ、150KB以下になった最初の値で止める
- 70未満まで下げないと収まらないものは書き出さず、ファイル名を報告する
- サイズずれは自動で拡縮せず、報告だけにする
- 書き出した後、fixed/ に check_creatives.py を実行して結果を貼る

試しに、ノイズの多い970×250のJPEGを書き出し直すと、容量はquality=95で254KB、85で158KB、70で128KBでした。85ではまだ150KBに届かず、70で収まる計算です。写真やグラデーションの多いバナーほど、この下げ幅が大きくなります。

サイズずれを自動で拡縮しない理由は、1ピクセルのずれが「素材の作り間違い」であることが多いからです。縦横比が変わるリサイズをかけると、文字や商品が歪みます。デザイン側に戻す判断は人が行い、Claude Codeには一覧の作成と再検査を任せます。

規定が変わったときの直し方

規定の見直しは、specs.jsonの数字を変えるだけで済みます。Claude Codeに「ヘルプの表とspecs.jsonを突き合わせて差分を出して」と頼めば、ページを開き直す手間も省けます。Claudeが読む対象はヘルプのページで、更新後の数字を書き込む前に、人が差分を見て確定させます。

運用の流れをまとめると、次のようになります。

手順

入稿規定チェックの回し方

  1. 1

    規定を表にする

    出典URLと一緒にspecs.jsonへ入れます。

  2. 2

    スクリプトを書かせる

    全ファイルを見る・読めないものもNGにする・終了コードで結果を返す、の3点を指示に入れます。

  3. 3

    入稿前に回す

    NGの一覧を直し、NGが0件になるまで繰り返します。

  4. 4

    境界のファイルは管理画面で確認する

    容量やFPSが境界に近いものは、広告管理画面の判定を最終とします。

同じ「表で持った規則に、ファイルを機械的に当てる」手法は、商品マスタの検算にも使えます。JANコードのチェックデジットを検算する例は、JANコードのチェックデジットを検算する手順で扱っています。

よくある質問

検査スクリプトをClaudeに毎回書き直させてもよいですか

おすすめしません。書き直すたびに判定の細部が変わり、先週通ったバナーが今週落ちる状況になりかねません。スクリプトはリポジトリに置いて固定し、規定の変更はspecs.json側で行います。

画像の中身(文字が切れていないか等)も検査できますか

この記事のスクリプトが見るのは、形式・寸法・容量・GIFのアニメーション設定という機械的に決まる項目だけです。文字の見切れや表現の審査は、画像を見て判断する別の作業になります。

この記事を共有:XはてブLinkedIn