KDP出版ノウハウ

KDP自動化スクリプトが深夜に止まり続けた原因と5時間かけて気づいたタイミングバグの話

KDP自動化スクリプトが深夜に止まり続けた原因と5時間かけて気づいたタイミングバグの話

この記事は約 8 分で読めます

📖 目次
  1. 📌 何が起きたか
  2. 📌 環境
  3. 📌 詰まったポイント
  4. 📌 解決までの手順
  5. 📌 コード/設定の抜粋
  6. 📌 試してわかったこと
  7. 📌 まとめ
  8. 📌 関連記事

最終更新: 2026-04-28

何が起きたか

KDP自動出版パイプラインを深夜に回したら、価格設定ページで止まり続けた。スクリプトは正常終了を報告しているのに、Kindle管理画面を開くと本が「下書き」のまま。翌朝また回す→また止まる。これを2回繰り返した。原因は「価格フォームの入力は通っているが、保存前に次ページへ遷移しようとしていた」という単純なタイミングバグだった。気づくまでに合計で5時間くらい溶けた。


環境

  • macOS Sequoia / MacBook Pro M5 32GB
  • Claude Code(Sonnet)でスクリプト修正
  • Playwright(非同期)で KDP ダッシュボードを操作
  • スクリプト: 01_Scripts/kdp/kdp_ai_book_generator.py
  • 価格設定ロジック: 同ファイル内 _set_pricing() メソッド

詰まったポイント

最初の失敗ログがこれ。深夜5時過ぎに回したバッチだ。

価格フォームにはちゃんと数字が入っている。KDPの「マーケットプレイスごとの価格設定」テーブルに 9.99 USD が表示されている。見た目は正常。

ところが「保存して続行」ボタンを叩いた直後、ページが遷移せずに止まる。

URLは https://kdp.amazon.co.jp/title-setup/kindle/.../pricing のまま動かない。エラーメッセージも出ない。ただ固まっている。

スクリプト側のログには "pricing saved: True" と出ているのに、ブラウザ上では保存が完了していなかった。

問題の箇所を抜き出すとこう↓。

# 修正前(バグあり)
await page.fill('input[data-testid="price-input-US"]', "9.99")
await page.click('button[data-testid="save-and-continue"]')
await page.wait_for_url("**/review**", timeout=10000)

fill() は DOM の value を書き換えるが、React の state までは更新しない場合がある。KDPのフォームはReact製で、fill() 直後に save-and-continue を押すと「未入力」扱いでバリデーションが通らないことがある——これが推測だったが、後で page.evaluate() で確認して合っていた。


解決までの手順

ステップ1 — 「保存済み」と「実際に保存された」を分離して確認する

page.fill() 後に入力値を page.input_value() で読み直して、期待値と一致しているか確認するコードを追加。

filled_val = await page.input_value('input[data-testid="price-input-US"]')
assert filled_val == "9.99", f"入力値不一致: {filled_val}"

これは通った。入力値は合っている。

ステップ2 — React の state 更新をトリガーする

fill() だけでは React が変化を検知しないパターンがある。dispatch_event()input イベントを明示的に送った。

await page.fill('input[data-testid="price-input-US"]', "9.99")
await page.dispatch_event('input[data-testid="price-input-US"]', "input")
await page.dispatch_event('input[data-testid="price-input-US"]', "change")

これで React side effect が走り、ボタンが「押せる状態」に変わった(DOM の disabled 属性が消えるのを wait_for_selector で確認できた)。

ステップ3 — ボタンが enabled になるまで待ってから叩く

await page.wait_for_selector(
    'button[data-testid="save-and-continue"]:not([disabled])',
    timeout=8000
)
await page.click('button[data-testid="save-and-continue"]')

クリック後の wait_for_url タイムアウトも 10s → 20s に伸ばした。KDP の画面遷移はサーバーサイド処理が入るので遅い。

ステップ4 — 再実行して出版前状態を確認

レビューページへの遷移を確認。/pricing から /review へ移動している。あとは publish ボタンを叩くだけの状態になった。


コード/設定の抜粋

_set_pricing() の修正後の核心部分だけ。

async def _set_pricing(self, page, price_usd: float) -> bool:
    price_str = f"{price_usd:.2f}"

    # 入力
    selector = 'input[data-testid="price-input-US"]'
    await page.fill(selector, price_str)

    # React state 更新を強制
    await page.dispatch_event(selector, "input")
    await page.dispatch_event(selector, "change")

    # ボタンが有効になるまで待つ
    btn = 'button[data-testid="save-and-continue"]:not([disabled])'
    try:
        await page.wait_for_selector(btn, timeout=8000)
    except TimeoutError:
        self.logger.error("保存ボタンが有効化されなかった")
        return False

    await page.click(btn)

    # 遷移確認
    try:
        await page.wait_for_url("**/review**", timeout=20000)
        return True
    except TimeoutError:
        current = page.url
        self.logger.error(f"遷移失敗: {current}")
        return False

KDP の URL パターンはロケールによって微妙に変わるので **/review** のグロブが安全。


試してわかったこと

React製フォームは fill() で見た目が変わっても内部 state が追いついていないことがある。特に KDP・note・coconala といった「保存ボタンが動的に disabled/enabled を切り替えるタイプ」の画面で詰まりやすい。

手順を整理すると:
fill()dispatch_event("input") + dispatch_event("change")wait_for_selector(:not([disabled]))click()wait_for_url()
この5ステップを全部踏まないと「保存した気になっているだけ」になる。

タイムアウト値は「体感より2倍長く」がちょうどいい。KDP のサーバー処理は夜中でも10〜15秒かかることがある。焦って短くすると夜中に失敗を量産する。

あと「スクリプトが成功ログを出した」と「実際に保存された」は別の話だと今回痛感した。成功判定のロジックを「URL遷移の確認」に変えてから、偽陽性がゼロになった。


まとめ

React フォームは fill() だけでは state が更新されない場合がある。dispatch_event("input")change を両方送ること。ボタンの disabled 解除を待ってからクリックし、URL 遷移で成功を確認するまでがセット。「保存された」と「保存されたログが出た」は別物——これだけで夜中の積み重ねは防げる。

📘 この記事のテーマをさらに深掘りした本

Claude Codeで副業自動化——月5万円を目指した全記録

非エンジニアが1年で月10万円に到達した全パイプラインと失敗談

Kindleで読む →


一ノ瀬泰斗のアバター
一ノ瀬泰斗
AI自動化エンジニア / Python個人開発者

Claude Code × Ollama × launchd で SNS・ブログ・KDPを全自動化。実測データと失敗談を軸に、月5万円収益化のリアルな記録を発信中。

💬 自動化の相談・小規模受託も受付中:「launchd で毎朝 AI が動く仕組みを作りたい」「KDP の自動出版を組みたい」など、X (@taito_automate) の DM からお気軽にどうぞ。


関連記事

✨ AUTHOR'S KDP BOOKS

かかる人向ケ、10分でわかるAI自動化入門

Claude Code / Ollama / launchd の実践テクニックをコンパクトにまとめたシリーズ。非エンジニアの会社員向けに書いてます。

Amazonで見る ›

✨ FOLLOW ME

AI自動化の実験・失敗・実測データを毎日発信中

𝕏 フォローする