ブラウザ自動化でフォーム入力が崩れるときの対処法|1文字ずつ打つのをやめる
こんにちは、すきまログです。
Playwright のような自動化ツールで文字を入力する処理、意外とハマりどころが多いです。今日は「1文字ずつ打ち込む」方式から離れて、コピペと同じ仕組みに切り替えたら安定した、という話を書きます。
「打ち込む」方式は入力先を選ぶ
最初に書いたスクリプトは、type() でキーボード入力を模して1文字ずつ打ち込んでいく方式でした。シンプルな入力欄なら問題なく動くのですが、リッチテキストエディタのような装飾付きの入力欄だと様子が変わります。
入力の途中で表示が更新されて、カーソル位置がずれる
特定の記号や改行のタイミングで、想定していない挙動になる
長い文章になるほど、途中で失敗する確率が上がる
入力欄の作り方によって挙動が変わるので、「動く入力欄」と「動かない入力欄」が出てきてしまい、対応がその都度場当たり的になっていました。
コピペと同じ仕組みを使う発想
そこで試したのが、人がコピー&ペーストしたときと同じ仕組みをそのまま使う方法です。1文字ずつ打ち込むのではなく、あらかじめクリップボードに文章を入れておき、貼り付け操作そのものを再現します。
// クリップボードに入れてから、貼り付けのキー操作をそのまま送る
await page.evaluate((t) => navigator.clipboard.writeText(t), text);
await page.locator(SELECTOR).click();
await page.keyboard.press('Control+V');考え方としては単純で、「人が普段やっている操作を、そのまま機械にもやらせる」というだけです。1文字ずつのキー入力より、エディタ側からすると自然な形の操作になります。
切り替えて分かった安定性の違い
この方式に変えてから、多くのリッチテキストエディタで安定して動くようになりました。
カーソル位置のズレによる文字化けがほぼなくなった
長い文章でも失敗率が下がった
エディタの種類ごとに個別対応する必要が減った
一方で、貼り付け操作を検知して独自の処理を挟んでいるようなエディタでは、この方法でもうまくいかないことがあります。万能ではなく、「打ち込む」方式より当たる確率が高い、という程度の理解にしています。
つまずいたポイント
貼り付け先の要素にきちんとフォーカスが当たっているかを確認しないまま実行し、意図しない場所に入力されてしまったことがあった
実行直後に画面を確認しても、描画のタイミングによっては入力結果が反映される前のスクリーンショットになってしまい、成功したかどうかの判断を誤りかけた
JavaScript で直接 value に代入する方法も試したが、これは避けた方がいい。 見た目には入るのに、React や Vue で作られた画面だとフレームワーク側が変更を検知できず、送信しても保存されないことがある。しかもエラーは出ない
結局いちばん効いたのは「入力したあとに読み返す」こと
方式をどれだけ工夫しても、失敗するときは失敗します。決定的だったのは、入力するたびに value を読んで一致を確認するようにしたことでした。
await page.keyboard.press('Control+V');
const actual = await page.locator(SELECTOR).inputValue();
if (actual !== text) throw new Error('入力が入っていない: ' + actual.slice(0, 40));「入力した」というツールの戻り値は当てになりません。送信まで通って「更新しました」と表示されるのに、実データを見ると変わっていない、ということが同じ日に何度も起きました。エラーが出ないぶん、確認を挟まないと一生気づけません。
すきま時間の使い方として
入力系の自動化は「動けばいい」で済ませると、エディタの実装が変わった瞬間に壊れます。1文字ずつの打ち込みで無理に頑張るより、人がやっている操作そのものに寄せる方が、結果的に手間も少なく安定すると感じています。