AI自動化・実践

非エンジニアがPlaywrightの定時実行を止めずに動かす方法

2026-06-14 · 最終確認 2026-10-10

Playwrightが必要になるのはどんな場面か

結論から言うと、Playwrightが必要になるのは、APIが用意されていないサービス、またはAPIだけでは操作が完結しないサービスに対して、ブラウザ操作そのものをコードにして自動実行したいときです。

FIG 01 — Playwright の定時実行が止まる場所
  1. 01起動タスクスケジューラが決まった時刻に実行
  2. 02ログイン保存したセッションで入る(切れたら通知)
  3. 03操作セレクタで画面を操作(変わったら失敗を記録)
  4. 04確認結果の件数とスクショを残す● 人が確認
セッション切れ・画面変更・PCのスリープ。それぞれ検知して通知する仕組みを先に入れる。

note.comの投稿やWordPressの管理画面のように、決まった形式でデータをやり取りするAPIが公開されていない、あるいは一部の操作だけAPIがあっても肝心の画面遷移や入力欄の操作はブラウザ側でしか行えない、という場面は珍しくありません。こうした場合に残る選択肢は、人間がブラウザで行っている操作を一つずつコード化していくことです。Playwrightは、Chromium・Firefox・WebKitをプログラムから直接操作し、クリック・入力・画面遷移の待機といった一連の動作をPythonやJavaScriptで記述できるようにするための道具です。

環境構築の手順やセレクタの基本的な書き方は、すでに丁寧な解説が公開されています。この記事では入門の段階はDevelopersIOの環境構築記事に譲り、実際に自分のサイトで定時実行させた結果どこで壊れ、何を直したかを中心に書きます。環境構築が終わっていて、動かしてはみたものの数日で止まる、という段階の方を想定しています。

gpirot.comで動いている2つの現場

私が運営するgpirot.comでは、APIが存在しないか、APIだけでは完結しない2つの場面でPlaywrightを使っています。note.comへの記事自動投稿と、WordPressテーマのカスタマイザー設定変更です。

note.com向けの自動投稿は run_note_publisher.py というPythonスクリプトが担当しています。処理の流れは次のとおりです。

  1. 保存済みのセッション情報を読み込んでnote.comにアクセスする
  2. ログイン画面が出ていた場合のみ、IDとパスワードを入力してログインする
  3. 記事タイトルをタイトル欄に入力する
  4. AIが生成した本文をエディタに流し込む
  5. アイキャッチ画像を設定する
  6. 公開ボタンをクリックし、公開完了の表示が出るまで待つ

この処理はWindowsのタスクスケジューラから定時で起動しています。サイト自体はCloudflare Pagesで公開していて、記事データや実行ログはSupabaseに保存しています。Playwrightのスクリプトは、この定時実行の仕組みの中で「実際にブラウザを操作して投稿する役」を担っている部分です。

正直に言うと、この記事で紹介する対策を入れる前の失敗件数を厳密に記録していたわけではないため、何件中何件が失敗していたかという比較数字はお見せできません。ただ、ログイン画面に戻される・途中で止まるという症状が週に何度も起きていた状態から、対策後はその症状でタスクが止まることがほぼなくなった、という体感の変化はあります。

もう1つの現場であるWordPressカスタマイザーの設定変更も、管理画面を手作業で開いて設定を変える操作をそのままコード化したものです。管理画面にログインし、該当の設定項目を操作して保存する、という流れをPlaywrightで再現しています。この現場特有の詰まりどころは、後述のWordPressの章でまとめて説明します。

詰まった場所1:セッション切れとログインの壊れやすさ

結論としては、毎回ログインからやり直す設計は壊れやすく、ログイン済みのセッションを保存して再利用する設計に変える必要がありました。

最初に組んだ run_note_publisher.py は、起動のたびにログインからやり直す作りでした。これがすぐに壁になりました。ログイン処理を毎回通すと、reCAPTCHAや2段階認証のような「人間であることの確認」が挟まる確率が上がり、定時実行のつもりが気づいたらログイン画面で止まっていた、ということが起きたのです。

そこで、一度ログインに成功したセッション情報をファイルに保存し、次回以降はそれを読み込んでログイン処理自体をスキップする設計に変えました。実際の手順は次のとおりです。

  1. 初回だけ通常どおりログイン処理を実行する
  2. ログイン成功を示す画面上の要素が表示されるまで待つ
  3. ログイン直後に context.storage_state でセッション情報をファイルに保存する
  4. 次回起動時は、保存したファイルを読み込んだ状態でブラウザコンテキストを作る
  5. アクセス直後にログイン画面が表示されているかを判定し、出ていれば通常のログイン処理に切り替える
from playwright.sync_api import sync_playwright

SESSION_FILE = ".note_session.json"

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    context = browser.new_context(storage_state=SESSION_FILE)
    page = context.new_page()
    page.goto("https://note.com/")
    # セッションが有効ならここでログイン画面は出ない

詰まりやすいのは、保存するタイミングです。処理の一番最後にまとめて保存しようとすると、途中でエラーが起きたときに保存自体が実行されず、次回もまたログインからやり直しになります。ログイン成功を確認できた直後、できるだけ早い段階で保存するようにしています。

もう1つの注意点は、セッションには有効期限があることです。一定期間が経つとファイルを読み込んでもログイン済み扱いにならず、結局ログイン画面に戻されます。この場合に備えて、保存したセッションを過信せず、ログイン画面の有無を毎回判定して通常ログインに切り替える分岐を残しています。この分岐を省略すると、期限切れのタイミングでスクリプト全体がエラー終了し、タスクスケジューラのログにだけ失敗が記録されて原因が分からない、という事態になります。

詰まった場所2:画面遷移前に次の操作が走って失敗する

結論としては、ボタンをクリックした直後に次の操作を書くと、画面の切り替わりを待たずにコードが先に進んでしまい失敗します。この対策として、画面側の状態を確認してから次に進む書き方に変えました。

典型的な失敗はこうです。公開ボタンをクリックした直後に、次のページにある要素を探しに行くコードを書くと、ページの読み込みがまだ終わっていないためにその要素が見つからず、エラーで止まります。人間が操作していれば画面が切り替わるまで自然と待っているのですが、コードはその間を自動では待ってくれません。

この問題は、処理を待たせる命令を間に挟むことで解決します。待機の考え方はPlaywrightのベストプラクティスを扱ったQiitaの記事でも整理されていますが、私が実際に使い分けている方法は次のとおりです。

命令何を待つか使う場面
wait_for_selector指定した要素が画面に現れるまで待つクリック後に次に操作したい要素が出てくるのを待つとき
wait_for_navigationページ遷移(URLの変化)が完了するまで待つボタンクリックで別ページに移動する処理のとき
固定時間の待機決まった秒数だけ待つ上記2つで判定しづらい、読み込みに時間差があるときの最終手段

基本の手順は次のとおりです。

  1. クリックしたい要素を特定してクリックする
  2. クリック後に表示されるはずの文言や要素を決めておく
  3. wait_for_selector でその要素が出るまで待つ
  4. 要素が出たことを確認してから、次の入力や判定処理に進む
page.click("button.publish")
page.wait_for_selector("text=公開しました")

詰まりやすいのは、待つ対象の要素を曖昧に決めてしまうことです。ページ内のどこにでもある汎用的な文言を待機条件にすると、前のページにも同じ文言があって「待ったつもり」で先に進んでしまうことがあります。公開後にしか出ない固有の文言や要素を待機条件に選ぶことが、この失敗を避ける実質的なポイントでした。固定時間の待機は手軽ですが、回線やサーバーの状態次第で足りなくなることがあるため、判定条件が明確に作れる場合はそちらを優先しています。

詰まった場所3:ヘッドレスモードは画面が見えずデバッグできない

結論としては、画面を表示しないヘッドレスモードで動かすと失敗時の原因調査が難しくなるため、処理の節目ごとにスクリーンショットを保存する仕組みを入れました。

定時実行するスクリプトは、画面を表示しないヘッドレスモードのChromiumで動かしています。タスクスケジューラ経由での実行では画面を表示する意味がないためです。ただしこれには副作用があり、エラーが起きたときに実際にどんな画面が表示されていたかが全く見えません。エラーメッセージの文字情報だけを見ても、どのページのどの要素でつまずいたのか分からず、原因調査に時間がかかっていました。

そこで、処理の節目ごとにスクリーンショットを保存する手順を追加しました。

  1. ログイン処理の直前にスクリーンショットを撮る
  2. ログイン処理の直後にスクリーンショットを撮る
  3. 本文や画像の入力が終わった直後にスクリーンショットを撮る
  4. 公開ボタンをクリックする直前と直後にそれぞれスクリーンショットを撮る
  5. ファイル名に処理のステップ名を入れて、あとから見て順番が分かるようにする
page.screenshot(path="logs/step_login_before.png")
# ...ログイン処理...
page.screenshot(path="logs/step_login_after.png")

目に見えない処理を、あとから見える形で残しておくという発想です。これを入れてからは、エラーログの文字だけを見て推測するよりも、どの段階まで進んでいたかを画像で確認できるようになりました。詰まりやすいのは、画像を撮りすぎてログフォルダが肥大化することです。処理が増えるごとに画像も増えるので、古いものを定期的に消す、あるいはエラーが起きた回だけ残すといった整理をしておかないと、あとで必要な画像を探しにくくなります。私は正常終了したログの画像は一定数を超えたら古いものから削除し、エラー終了したログの画像だけは消さずに残す、という分け方にしています。

WordPressカスタマイザー自動化で追加に効いた分岐

結論としては、セッション保存・待機・スクリーンショットという同じ3つの対策はWordPress側でもそのまま有効でしたが、管理画面特有の事情でもう一段階の分岐が必要になりました。

WordPressカスタマイザーの画面は、設定項目を変更するとプレビュー領域がiframeとして更新される作りになっています。note.comの公開ボタンのように画面全体が切り替わるわけではなく、同じページの中にある別の領域が非同期で書き換わるため、wait_for_selectorで待つ対象を「ページ全体の要素」ではなく「iframe内の要素」として指定し直す必要がありました。これに気づかず画面全体を対象にwait_for_selectorを書いていたときは、要素が永遠に見つからずタイムアウトで止まる、ということが起きています。

もう一つの違いは保存ボタンの挙動です。カスタマイザーの保存はAjaxによる非同期保存で、ボタンをクリックした直後は画面上の見た目がほとんど変わりません。保存が完了したことを確認するには、ボタンのラベルが「公開」から「公開済み」のような表示に変わる、あるいは保存完了を示す小さな通知が一瞬だけ表示される、といった細かい変化を待機条件にする必要がありました。note.comの「公開しました」のような分かりやすい文言がない分、この現場の方が待機条件の特定に時間がかかった部分です。

ログイン・セッション保存・スクリーンショットの仕組み自体はnote.com向けに作ったものをほぼそのまま流用できています。現場ごとに作り直したのは、この待機条件の部分だけでした。

非エンジニアがClaude Codeと組む実際のやり取り

結論としては、処理を入力・処理・出力・確認の4つに分けて考える習慣があれば、エンジニアでなくてもAIと対話しながらこの水準のスクリプトを組み、今回の3つの詰まりを解消できます。

私自身はエンジニアではありません。run_note_publisher.pyも、Claude Codeと対話しながら組み立てました。例えば、セッション切れの問題に気づいたときは、次のように状況を伝えています。

定時実行のタスクが週に数回、ログイン画面が表示された状態で
止まっています。実行後のスクリーンショット(step_login_after.png)
を確認すると、ログインボタンを押した直後の画面のままで、
その先のタイトル入力欄に進んでいません。
storage_stateでセッションを保存して再利用する形に
変更したいです。保存するタイミングと、保存したセッションが
無効だった場合の分岐も含めて直してください。

ここで役立ったのが、入力・処理・出力・確認の4分割です。入力は投稿するタイトルや本文、処理はPlaywrightによるブラウザ操作、出力は公開された記事、確認はスクリーンショットや実行ログにあたります。この切り分けがあると、どこで失敗したのかをAIに伝えるときも「処理の、ログインボタンをクリックした直後で止まっている」というように具体的に説明でき、スクリーンショットのファイル名も合わせて渡せるため、AI側も見当違いの修正を提案しにくくなります。実際、待機条件を曖昧なまま伝えたときは的外れな修正が返ってきたことがあり、エラーメッセージとスクリーンショットの両方をセットで渡すようにしてから、一度のやり取りで直る確率が上がった実感があります。

同じ構造は他の自動化にも応用できる

結論としては、入力・処理・出力・確認という4つの区分けは、Playwrightによるブラウザ操作に限らず、他の自動化ツールでも同じように使える考え方です。

PDFや写真を読み取って表形式のデータに変換するAI転記ツールは、入力(画像・PDF)、処理(AIによる読み取り)、出力(Excelに貼れる表データ)という同じ構造で組んでいます。また、問い合わせの自動返答ツールも、入力(問い合わせ内容)、処理(自動応答の判定)、出力(返答)、確認(管理画面での履歴確認)という形で作られています。

どちらのツールも、Playwrightのようにブラウザを操作するわけではありませんが、処理のどこで失敗しているかを切り分けるという考え方は共通しています。入力と出力の間に何が起きているかを分解して、それぞれの段階を確認できる形にしておく、という組み方自体がPlaywright以外の自動化にも応用できるということです。

まとめ:自分の手作業を書き出すところから始める

Playwrightが向いているかどうかを分けるのは、対象のサービスにAPIがあるかどうかではなく、自分が同じ操作を繰り返しているかどうかです。APIがあってもなくても、毎回手でクリックしたり入力したりしている作業があれば、それは自動化の候補になります。

次にやることとしては、自分が週に何度も手作業で繰り返しているブラウザ操作を、思いつく範囲で書き出してみることです。ログインして何かを入力して保存する、同じ設定画面を毎回開いて数値を変える、といった操作が見つかれば、それがPlaywrightで自動化できるかどうかを検討する最初の一歩になります。WordPressのAPIを使った投稿手順やnote自動投稿の線引きを扱った記事も合わせて公開していますので、ブラウザ操作以外の自動化を検討する際の参考にしてください。

同じような作業を自動にできるかどうか、無料でお返しします。いまのファイルと手順を見せてください。

お問い合わせ →