
ブラウザ自動化が壊れる原因と対策|Playwrightで動いた翌週に止まる
こんにちは、すきまログです。
Node.js と Playwright で画面をそのまま操作するタイプの自動化を組んだことがあるのですが、これが想像以上に壊れやすいものでした。今日はその壊れ方のパターンと、どう付き合っているかを書いてみます。
動いた翌週に動かなくなる
自動化と聞くと「一度作れば動き続けるもの」という印象があると思います。少なくとも私はそう思っていました。
ところが画面操作の自動化は違いました。自分は何も変えていないのに、ある日突然止まるのです。理由は単純で、操作している側ではなく、操作される側の画面が変わったからでした。
ボタンの位置や名前が変わる
それまで無かった確認画面が挟まる
表示の順番が入れ替わる
こちらのコードは1行も変わっていないので、原因を探すのに時間がかかります。
壊れ方には型がある
何度か踏むうちに、止まり方がだいたい3パターンに分かれることが見えてきました。
要素が見つからない:一番わかりやすい壊れ方です。エラーとして止まるので気づけます
想定外のものが前に出てくる:お知らせやお祝いの表示が画面をふさぎ、その裏のボタンを押そうとして失敗します。これも止まりますが、原因が「見えているのに押せない」なので分かりにくいです
エラーにならず、静かに間違える:一番怖いのがこれです。処理は最後まで通るのに、結果だけが違う。気づかないまま次の作業に流れていきます
3つ目に何度か刺されてから、最後に「意図した状態になったか」を確認する処理を必ず入れるようにしました。
つまずいたポイント
CSSクラス名に頼ったセレクタが真っ先に壊れた:画面の見た目が変わるとクラス名は変わりますが、「そこがボタンである」という役割は変わりません。getByRole や aria-label のような、見た目に依存しない指定に寄せたら壊れにくくなりました
// 壊れやすい(クラス名は見た目の変更で変わる)
await page.click('.btn-primary-2xk9');
// 壊れにくい(役割とラベルは変わりにくい)
await page.getByRole('button', { name: '公開する' }).click();待ち時間で解決しようとした:動かないときに waitForTimeout を延ばして対処すると、その場は直りますが原因は残ります。時間ではなく、目的の表示が現れたことを条件に待つ形へ書き直したら安定しました
// 時間で待つ(遅いうえに、遅い日には結局落ちる)
await page.waitForTimeout(5000);
// 状態で待つ
await page.locator('text=公開しました').waitFor({ timeout: 15000 });画面を読み込み直して状態を壊した:様子を見るつもりで page.reload() を挟んだところ、進めていた入力が消えてしまいました。途中の状態を持つ画面では、確認のつもりの操作が一番危ないです
プロファイルが使用中で起動できなかった:launchPersistentContext を使っていると、前回のブラウザプロセスが残っているだけで次回が起動に失敗します。起動前に古いプロセスを落とす処理を先頭に入れました
止まったときに何も残らなかった:後から原因を追えるように、失敗した時点で page.screenshot() を保存する処理を足しました。これが無かった頃は、再現させるところからやり直しでした
付き合い方を変えた
今は「壊れない仕組みを作る」ことは諦めていて、壊れたときにすぐ気づけることを優先しています。最後に結果を検算する、失敗したら記録を残す、そして最終確認は人が見る。この3つを守るだけで、実害はほとんど出なくなりました。
自動化は作って終わりではなく、直しながら育てていくものだと考えるようになりました。
すきま時間の使い方として
画面操作の自動化は、完璧を目指すと沼にはまります。まずは一番回数の多い1工程だけを自動にして、結果は自分の目で確かめる。この形なら、少しずつでも前に進められると感じています。