Playwrightで7サービスを自動操作して詰まった箇所(実運用の記録)

Playwrightを使って、note・X・Instagram・Bluesky・Tumblr・Amazonアソシエイト・YouTubeの操作を自動化しています。実際に運用してみて詰まった箇所と、その原因をまとめます。

1. headlessだと弾かれるサービスがある

Amazonアソシエイトの成果レポートを自動取得しようとして、4回連続で失敗しました。ログには毎回こう出ます。

[ERROR] ログインが切れています

実際にはログインは切れていませんでした。headless=Trueのときだけログイン画面に強制送還されていたのです。同じブラウザプロファイルのままheadless=Falseにすると、成果ページがそのまま開きました。

教訓は「ログインが切れています」と読めるエラーを鵜呑みにしないこと。画面ありで一度確かめてから判断してください。

2. has-text は部分一致なので危険

Playwrightのhas-text()部分一致です。これで2回実害が出ました。

  • button:has-text("保存")「下書き保存」を掴んだ
  • has_text("追加")「追加済」を掴み、再実行で登録を解除してしまった

かといって:text-is()子要素の文字を見ないため、アイコン付きボタンでは使えません。結局、候補を列挙してinner_text().strip()で突き合わせる方式に落ち着きました。

loc = page.locator(f'button:has-text("{label}")')
for i in range(min(loc.count(), 8)):
    b = loc.nth(i)
    if b.is_visible() and b.inner_text().strip() == label:
        b.click(); break

3. UIは予告なく変わる

noteのフォロー解除は、以前は「フォロー中」を押すと確認ダイアログが出る作りでした。これがドロップダウンメニューに変わり、メニュー内の「フォロー解除」を選ばないと外れなくなりました。

厄介だったのはエラーにならなかったことです。ボタンは押せているので処理は「成功」で終わり、実際には1件も解除されていませんでした。

対策は2つです。

  • 操作後に状態が変わったかを必ず確認する(押せた≠変わった)
  • 0件でも件数を数字でログに残す。「対象なし」とだけ書いていたため、3日間ゼロだったことに気づけませんでした

4. 無限スクロールのページで終了条件を間違えた

「ページの高さが変わらなくなったら終了」という条件を書いたところ、無限スクロールのページでは永久に成立せず、2時間の実行時間上限まで走り続けました。

正しくは「新しい対象が一定回数見つからなかったら終了」です。高さではなく成果で判定してください。修正後は36分で正常終了しました。

5. ログインセッションの持ち運びはプロファイル丸ごとにしない

ブラウザプロファイルは706MBありました。別のサーバーへ移すには重すぎます。

Playwrightのstorage_stateを使えば、CookieとlocalStorageだけを4KBのJSONで書き出せます。移行はこちらが実用的です。

まとめ

  • headlessで弾かれるサービスがある。失敗時はまず画面ありで確認する
  • has-textは部分一致。inner_textで完全一致を取る
  • 押せた≠変わった。操作後に状態を確認し、0件も数字で残す
  • 無限スクロールは「高さ」ではなく「成果」で終了判定する
  • セッションの持ち運びはstorage_state(4KB)