Claude Media
Claude Codeで手順書のスクリーンショットをPlaywrightで撮り直す — 台本化と一括更新

Claude Codeで手順書のスクリーンショットをPlaywrightで撮り直す — 台本化と一括更新

手順書の画像1枚ごとに操作を台本へ書き出し、Playwrightで一括して撮り直す流れです。撮るたびに変わる要素の固定と、Claude Codeへの任せ方を実測つきで載せます。

画面のデザインが変わるたびに、手順書の画像を人が撮り直す。この作業は、画像の枚数が増えるほど重くなります。この記事では、画像1枚ごとの操作を台本として書き出し、Playwrightで一括して撮り直す仕組みを作ります。台本の下書きと、UI変更で壊れた箇所の修正をClaude Codeに任せる部分も扱います。

撮り直しの自動化で最初にぶつかるのは、同じ画面なのに撮るたびに画像が変わる問題です。時刻表示、アニメーション、点滅するカーソルが原因で、差分が毎回出ます。デモページで実測した結果と、固定に使うオプションを先に示します。

手順書の画像1枚を、台本の1行にする

手順書の画像は、たいてい「画面を開く → 入力する → ボタンを押す → 結果を撮る」という操作の結果です。この操作が人の頭の中にしかないと、UIが変わったときに誰も再現できません。

そこで、画像1枚を台本1件として書き出します。台本に持たせるのは、出力ファイル名、撮影前の操作、写したくない場所の3つです。

[
  {
    "file": "01-top.png",
    "steps": [],
    "mask": ["#ts"]
  },
  {
    "file": "02-saved.png",
    "steps": [
      { "fill": ["#name", "山田"] },
      { "click": "#save" }
    ],
    "mask": ["#ts"]
  }
]

ファイル名は手順書の本文が参照する名前と一致させます。手順書側が ![保存後の画面](images/02-saved.png) と書いていれば、台本の file も 02-saved.png です。この対応が取れていれば、台本の全件を撮り直すだけで手順書の画像がすべて入れ替わります。

台本を読んで撮影するスクリプトは次のとおりです。Playwright 1.64.1(Chromium)で動作を確かめた最小形です。

import { chromium } from 'playwright';
import fs from 'node:fs';
 
const [, , base, outDir] = process.argv;
const shots = JSON.parse(fs.readFileSync('shots.json', 'utf8'));
fs.mkdirSync(outDir, { recursive: true });
 
const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 800, height: 360 },
  locale: 'ja-JP',
  timezoneId: 'Asia/Tokyo',
  colorScheme: 'light',
  deviceScaleFactor: 1,
});
 
for (const s of shots) {
  const page = await context.newPage();
  await page.goto(base);
  for (const st of s.steps) {
    if (st.fill) await page.fill(...st.fill);
    if (st.click) await page.click(st.click);
  }
  await page.screenshot({
    path: `${outDir}/${s.file}`,
    animations: 'disabled',
    caret: 'hide',
    mask: s.mask.map((m) => page.locator(m)),
  });
  await page.close();
}
await browser.close();

台本1件ごとにページを開き直しているのは、前の画像の状態を引きずらないためです。2枚目の「保存後」の画面が、1枚目の入力内容に依存しません。

実行は1行です。

node capture.mjs https://staging.example.com/settings images

撮るたびに変わるものを、実測で押さえる

固定のオプションが本当に効くのかを、デモページで確かめました。ページには「最終更新」として現在時刻を表示する要素と、回り続けるアイコン(CSSの無限アニメーション)を置いています。

1秒あけて2回撮影し、PNGのハッシュ値を比べた結果が次の表です。

撮影条件1回目と2回目の画像
オプションなし1回目と2回目の画像2枚とも別のファイルになった
animations: 'disabled'・caret: 'hide'・mask を指定1回目と2回目の画像2枚ともバイト単位で同一だった

オプションなしでは、時刻の文字とアイコンの回転角が違うだけで別の画像になります。手順書の画像を更新するたびにGitの差分が全部出てしまい、本当に画面が変わったのかが見分けられません。

指定した3つの意味は、Playwrightの page.screenshot の説明で次のように定められています。

  • animations: 'disabled': CSSアニメーション、トランジション、Web Animationsを止める。回数が決まっているものは終了状態まで進め、無限に続くものは初期状態に戻す
  • caret: 'hide': 入力欄の点滅するカーソルを隠す。既定値も hide なので、明示は読み手への目印になる
  • mask: 指定した要素を、その領域全体を覆う桃色(#FF00FF)の箱で塗りつぶす。色は maskColor で変えられる

この実測はローカルのHTML1枚での結果です。外部の画像、Webフォント、遅延読み込みを含む実際の画面では、これだけでは揃わないことがあります。そのときは次の項目を足します。

画面ごとに揃えておく設定

コンテキストを作るときに、撮影環境の差が出る項目を固定します。

設定揃える理由
viewport揃える理由画像の縦横サイズ、折り返し位置を決める
locale / timezoneId揃える理由日付、通貨、曜日の表記を決める
colorScheme揃える理由ライトとダークの取り違えを防ぐ
deviceScaleFactor揃える理由画像の解像度を決める

page.screenshot の側にも、画像を軽くしたいときの scale: 'css' があります。1CSSピクセルを1画像ピクセルにするので、高解像度の端末設定でも画像が大きくならず、手順書に貼る画像の容量を抑えられます。

写したくない場所は、塗るか消すか切り出す

手順書に載せる画像には、顧客名、メールアドレス、請求額が映り込みがちです。台本の mask に書いた場所は、撮影の時点で塗りつぶされます。画像をあとから加工する手順が要らなくなる点が、台本化の利点です。

塗りつぶしだけでは足りない場面向けに、手段が3つあります。

  • style オプション: 撮影の間だけ適用するCSSの文字列を渡す。広告枠やチャットの吹き出しのように、領域ごと消したいときに使う。ShadowDOMやiframeの中にも効く
  • 要素単位の撮影: page.locator('.header').screenshot({ path: 'header.png' }) のように、1つの要素だけを切り出す。画面全体を見せる必要がない手順で、周囲の情報を最初から写さずに済む
  • fullPage: true: スクロールが必要な画面を1枚で撮る。縦に長い画像になるので、手順書では見せたい範囲だけに絞るほうが読みやすい

どれを使うかは、台本のエントリごとに決められます。mask と同じく、台本に残しておけば誰が撮り直しても同じ画像になります。

落とし穴

マスクは画面上の要素にだけ効く

mask が覆うのは、指定したロケーターが指す要素です。画面内の別の場所に同じ個人情報が出ていると、そちらは塗られません。撮り直しのたびに画像を目で確かめる工程は、残してください。

台本の下書きと修正をClaude Codeに任せる

台本の下書きはClaude Codeに任せられます。手順書の本文を読めば、どの画像がどの操作の結果かが書いてあるからです。ここでは claude -p で一度きりの実行を走らせます。

claude --bare -p "docs/manual.md の画像参照を全部拾い、直前の手順文から \
shots.json のエントリを作る。撮影は node capture.mjs だけを使う" \
  --append-system-prompt-file docs/shots-rules.md \
  --allowedTools "Read,Edit,Bash(node capture.mjs *)"

各オプションを選んだ理由は次のとおりです。

  • --bare は、手元のフックやプラグイン、CLAUDE.mdを読み込まずに起動するモードです。マシンごとの差が結果に混ざらないので、スクリプトやCIでの利用が勧められています。サブスクリプションのログインは使われないため、ANTHROPIC_API_KEY を環境変数に設定してから実行します
  • --bare ではCLAUDE.mdが読まれません。台本の書き方の決まりは、--append-system-prompt-file でファイルとして渡します
  • --allowedTools に Bash(node capture.mjs *) と書くと、撮影スクリプトの実行だけが確認なしで通ります。それ以外のシェルコマンドは許可されません

渡すルールファイルの例です。

# shots.json の書き方
- file は手順書の画像参照と同じ名前にする
- セレクターは data-testid を最優先にし、無ければ role と name を使う
- 時刻、乱数、ユーザー名が出る要素は必ず mask に入れる
- 台本にないページへ移動しない。ログイン画面は撮らない
- 実行は node capture.mjs だけ。結果は images/ に出す
- 台本のほかのエントリと capture.mjs は触らない

claude -p は成功すると終了コード0で終わり、失敗すると0以外で終わります。これをシェルスクリプトで分岐に使えます。

台本が壊れたら、Claude Codeに修正させる

UIが変われば、台本のセレクターが画面から消えます。デモで存在しないボタンを押させたところ、Playwrightは待機時間を超えたあとにエラーを出し、スクリプトは終了コード1で終わりました。この性質が、更新の仕組みに向いています。

つまり、撮影スクリプトが通ったかどうかは、終了コードだけで判断できます。0なら全部撮れています。1なら、ログの最後に書かれた失敗の理由を、次のようにClaude Codeへ渡せます。

node capture.mjs "$BASE_URL" images 2> capture-error.txt || \
claude --bare -p "capture-error.txt のエラーは、画面のUI変更でセレクターが \
消えたものか。shots.json の該当箇所だけ直して、もう一度撮影して" \
  --append-system-prompt-file docs/shots-rules.md \
  --allowedTools "Read,Edit,Bash(node capture.mjs *)"

直させる範囲は shots.json の該当エントリに限ります。--bare ではCLAUDE.mdが読まれないので、先ほどのルールファイルを同じように渡し、そこに書いた「台本のほかのエントリと capture.mjs は触らない」が効くようにします。ほかの画像の撮り方が勝手に変わる事故を避けられます。

撮り直したあとの確認

画像が更新されたら、手順書の文章とずれていないかを見ます。ここも順番を決めておくと速くなります。

手順

撮り直しの流れ

  1. 1

    台本の全件を撮影する

    node capture.mjs を実行し、終了コードが0であることを確かめます。

  2. 2

    Gitの差分で変わった画像を絞る

    git status images/ で、中身が変わった画像だけを見ます。固定ができていれば、画面に変更がない画像は差分に出ません。

  3. 3

    変わった画像を手順文と突き合わせる

    画像のパスをClaude Codeに渡し、直前の手順文と矛盾がないかを尋ねます。「ボタン名が文中と違う」「枠が新しい」といった指摘が返ります。

  4. 4

    手順書の文面を直す

    ボタン名や配置が変わっていれば、画像に合わせて文章を直します。

Claude Codeに画像を見せるには、会話に貼る方法のほか、Analyze this image: /path/to/your/image.png のように画像のパスを書く方法があります。撮り直しの現場では後者が使いやすく、台本の file と同じパスを指定すれば、更新した画像の全部を順に尋ねられます。

判断は人に残します。画像の違いが意図した画面変更なのか、台本のミスなのか、撮影時のデータのせいなのかは、画像だけからは決められません。Claude Codeには「手順文と食い違う箇所の候補」を挙げさせ、採否は手順書の担当者が決めます。

認証が要る画面と環境の揃え方

ここまでのデモは、ログインの要らないローカルのページでした。社内システムの手順書では、ログイン後の画面を撮ることがほとんどです。ログイン状態の持ち回しと秘密情報の扱いは、PlaywrightのstorageStateでログイン状態を使い回す手順にまとめています。撮影用には、本番ではなくステージングの環境と、撮影専用のアカウントを用意しておくと、顧客データが映り込みません。

撮影環境そのものを固定したい場合は、Dockerの定期ジョブにする構成が参考になります。ブラウザやフォントの環境が担当者のマシンごとに違うと、同じ台本でも画像が揃わないことがあるからです。

関連する記事との役割の違い

手順書の画像に関わる作業は、方向が3つに分かれます。

画面の変更が頻繁なシステムでは、画像から文章を起こす手動の更新と、この記事の台本の更新が併存することもあります。台本が整っている画面は自動で、新しく増えた画面は画像から起こす、という使い分けです。

まとめ

手順書の画像は、台本があれば撮り直しの作業になります。撮るたびに変わる要素は animations・caret・mask と環境設定で押さえ、台本が壊れたら終了コードで検知してClaude Codeに該当箇所だけ直させます。最初の台本づくりに手間はかかりますが、UI変更のたびに全画像を手で撮り直す作業は、1コマンドの実行と差分の確認に置き換わります。

台本をリポジトリに置くと、手順書の画像がいつの画面のものかをGitの履歴から追えるようになります。そこから先の定期実行や、差分が出たときの通知に広げるかどうかは、画面の変更頻度で決めるとよいでしょう。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/headless
  2. playwright.dev/docs/screenshots
  3. playwright.dev/docs/api/class-page
  4. playwright.dev/docs/test-use-options
この記事を共有:XはてブLinkedIn