心理学で、
人間関係を
ちょっとラクにする。
MBTIとアドラー心理学を軸に、
自分と他者を理解するヒントを発信しています。
これで実際のスクリーンショット・コードの内容が確認できた。記事を書く。
KDP自動出版パイプラインが3日連続で詰まった。原因はカテゴリー設定の「場所」チェックボックスという、地味すぎる一点だった。
何が起きたか
深夜5時に自動起動した kdp_auto_publish.py が、毎回「コンテンツ」か「詳細」ページで止まって出版まで届かない。ログには category_placement_fail_050131.png というスクリーンショットが残っていて、モーダルが開いたまま保存できていないのはわかった。でも「なぜ開いたままなのか」がしばらく読めなかった。
環境
macOS Sequoia 15.x / MacBook Pro M5 32GB
Python 3.12 + Playwright (asyncio)
スクリプト: 01_Scripts/kdp/kdp_auto_publish.py v3.0
カテゴリー設定フィールド: category_path / category_path_2 / category_placement
詰まったポイント
KDPのカテゴリーモーダルは2段階構造になっている。
- ドロップダウンで「コンピューター」「Python」と絞り込む
- そのあと「場所」というチェックボックス群が出てきて、
コンピュータ・IT > Pythonみたいなノードを選択する
スクリプトの category_placement というフィールド、デフォルト値がこうなっていた。
placement = config.get("category_placement", "日本の小説・文芸")
技術書のキューに category_placement を指定し忘れると、スクリプトが「日本の小説・文芸」というテキストをモーダルの中から探す。Pythonの技術書ページにそんなテキストは当然ない。見つからないので category_placement_fail のスクリーンショットを保存してフォールバック処理に入る——が、フォールバックも「最初の未チェックのチェックボックスを選択」という実装で、タイミングによってはモーダル外のDOM要素を掴んでいた。
このモーダルが閉じないまま「次へ」ボタンをクリックしようとして、コンテンツページで止まる。
EPUBアップロード自体は成功していた。「表紙のアップロードに成功しました」は出ている。でもその後の「保存しています…」スピナーが永遠に回り続けた。
解決までの手順
ステップ1: デバッグスクリーンショットで詰まり箇所を特定する
04_Config/kdp_debug/ に保存された category_placement_fail_050131.png を開いて、カテゴリーモーダルが開いたままになっているのを確認した。ログの category_placement_fail というファイル名はコード内でこのタイミングにだけ使われているので、即座に行番号まで辿れた。
# kdp_auto_publish.py:1639
await save_debug(page, "category_placement_fail")
ステップ2: placement のデフォルト値を疑う
コード上のデフォルト "日本の小説・文芸" が、技術書カテゴリーのモーダルに存在しないと確認した。KDPの実UIで手動確認したところ、Pythonカテゴリーの「場所」テキストは コンピュータ・IT だった。
ステップ3: キューJSONに category_placement を追加する
{
"category_path": ["コンピュータ・IT", "Python"],
"category_path_2": ["コンピュータ・IT", "プログラミング"],
"category_placement": "コンピュータ・IT"
}
category_path で絞り込んだ後に出てくる「場所」チェックボックスのテキストと一致させる必要がある。「コンピューター」じゃなくて「コンピュータ・IT」——中黒と表記揺れが普通にある。
ステップ4: フォールバックのスコープを修正する
フォールバック処理が document 全体のチェックボックスを走査していたのが根本の問題だった。モーダルが閉じていてもページ内の別チェックボックスを掴んでいた。
# 修正前: document 全体
var container = modal || document;
# 修正後: aria-hidden="false" のモーダルに限定
var modal = document.querySelector(
'.a-popover-modal[aria-hidden="false"], [role="dialog"][aria-hidden="false"]'
);
if (!modal) return {status: 'modal_not_found'};
var container = modal;
モーダルが存在しない場合は即 modal_not_found を返して処理を中断する。ゾンビクリックが消えた。
ステップ5: 再実行して通過確認
コンテンツページで詰まらずに価格設定ページまで進むことを確認した。
コード/設定の抜粋
キューJSONのカテゴリー設定テンプレ(技術書版):
{
"category_path": ["コンピュータ・IT", "Python"],
"category_path_2": ["コンピュータ・IT", "データサイエンス・機械学習"],
"category_placement": "コンピュータ・IT"
}
フォールバック修正の核心部分:
fallback_result = await page.evaluate("""() => {
var modal = document.querySelector(
'.a-popover-modal[aria-hidden="false"], [role="dialog"][aria-hidden="false"]'
) || document.querySelector('.a-popover-modal[style*="visible"]');
if (!modal) return {status: 'modal_not_found'};
var cbs = modal.querySelectorAll('input[type="checkbox"]');
for (var i = 0; i < cbs.length; i++) {
if (!cbs[i].checked) {
cbs[i].click();
return {status: 'checked', index: i};
}
}
return {status: 'none_available'};
}""")
launchd で定時起動する場合の plist 抜粋:
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key><integer>5</integer>
<key>Minute</key><integer>0</integer>
</dict>
試してわかったこと
category_placement のデフォルト値を小説前提にしていたのが根本ミスだった。KDPのカテゴリーツリーは「場所」という概念で、選択した階層に対して「どのノードに置くか」を別途チェックさせる構造になっている。ドロップダウンで選んだ値と「場所」のテキストが必ずしも一致しない——これがわかっていなかった。
あとモーダルの aria-hidden 属性を信頼するようにしたら、「開いてないモーダルを操作しようとする」問題が全体的に減った。Playwrightで動的モーダルを扱うときは aria-hidden="false" を必ずスコープに含めるべきだと思った。
デバッグスクリーンショットの命名規則は最初から入れておいて本当によかった。ログの文字列だけだと「どの画面で止まったか」の特定に時間がかかる。
まとめ
category_placementのデフォルトが小説前提で技術書に全滅していた- フォールバックが
document全体を走査していてゾンビクリックが発生していた - モーダルスコープを
aria-hidden="false"に限定したら解決した
