AI自動化

WantedlyのPlaywright自動化で3回連続404——CDPセッション越しにフォームへ辿り着くまでの1時間

Ichinose Taito — MBTI × 心理学 × AI

心理学で、
人間関係を
ちょっとラクにする。

MBTIとアドラー心理学を軸に、
自分と他者を理解するヒントを発信しています。

記事を読む ›
ちのくん
ちのくん
— こんな活動をしています —
WantedlyのPlaywright自動化で3回連続404——CDPセッション越しにフォームへ辿り着くまでの1時間

スクリーンショットで状況が全部把握できた。記事を書く。


何が起きたか

WantedlyのプロフィールをPlaywright + CDPで自動入力しようとしたら、スクリプトが3回連続で404に飛ばされた。/users/edit を叩けばフォームに辿り着けると思っていたのに、「Oops… Error 404」ページが出るだけ。ログには正常終了と出ている。セッション切れか、URLが変わったか、それともサービス側の仕様変更か——原因の切り分けに1時間近く使った。

環境

  • macOS Sequoia / MacBook Pro M5 32GB
  • Python 3.12 + Playwright(CDP経由でBraveに接続)
  • Claude Code でスクリプト生成・デバッグ
  • Brave 起動: launch_brave_debug.sh--remote-debugging-port=9222
  • Ollama(qwen3.5:9b)でログ要約

詰まったポイント

最初のエラーは単純に見えた。page.goto("https://wantedly.com/users/edit") → 404。「URLが古いんだろう」と思って /users/me/edit に変えても同じ。/profile/edit でも同じ。

スクリーンショットで確認したら、ページ自体はちゃんと読み込まれていた。Wantedlyのドメインに到達して、セッションも生きていて、それでも404。

URLの問題じゃなくて、「そのページはアカウントの状態的に存在しない」という判定をWantedly側がしているわけだ。

次に気づいたのが、初回ログイン後に必ずウィザードが挟まるという構造。「あなたの新しいIDを設定」という画面で、wantedly.com/id/{ユーザー名} 形式のURLを確定させないとプロフィール編集ページ自体が存在しない——という仕掛けだった。スクリプトはこのウィザードを完全に無視していた。

goto で編集ページに飛ぼうとしても、ウィザード未完了の状態だから弾かれる。当たり前なんだけど、ハードコードしたURLを叩けば直接行けるという思い込みが邪魔をしていた。

解決までの手順

ステップ1: 遷移後のURLをログに残す

goto() の直後に page.url を取る一行を入れた。これで「どこにリダイレクトされたか」が見えるようになった。

await page.goto("https://wantedly.com/", wait_until="domcontentloaded")
current_url = page.url
print(f"[debug] 遷移後URL: {current_url}")

ステップ2: ウィザードの検出と通過ロジックを追加

URLに setup が含まれるか、h1 テキストで「IDを設定」が見えたらウィザードフローに入るようにした。

if "setup" in page.url or await page.locator("h1:has-text('IDを設定')").count() > 0:
    await handle_wizard(page)

async def handle_wizard(page):
    await page.wait_for_selector("button:has-text('次へ')", timeout=5000)
    await page.locator("button:has-text('次へ')").click()
    await page.wait_for_load_state("networkidle")

ステップ3: ウィザード完了後のプロフィールURLを動的に取得

ウィザードで設定したIDが https://wantedly.com/id/{username} になる。アカウントによって変わるので、完了後に page.url から抜き出す形にした。

await page.wait_for_url("**/id/**")
profile_url = page.url  # → https://wantedly.com/id/yasushi_tsuchiya_b
username = profile_url.rstrip("/").split("/")[-1]

ステップ4: 編集ページへの遷移をURL直打ちからボタンクリックに変更

/users/edit に直接 goto するのをやめて、プロフィールページを開いてから「プロフィール項目を追加・生成」ボタンを押す方式に切り替えた。これでウィザード済みかどうかに関係なく動くようになった。

await page.goto(profile_url)
await page.wait_for_load_state("domcontentloaded")
edit_btn = page.locator("text=プロフィール項目を追加・生成").first
await edit_btn.click()

ステップ5: Reactフォームへの入力

Coconalaのときと同じパターンが出た。fill() で値を入れてもReactコンポーネントに伝わらない。nativeセッター + dispatchEvent の組み合わせで解決。

await page.evaluate("""
    (args) => {
        const el = document.querySelector(args.selector);
        const setter = Object.getOwnPropertyDescriptor(
            window.HTMLTextAreaElement.prototype, 'value'
        );
        setter.set.call(el, args.value);
        el.dispatchEvent(new Event('input', { bubbles: true }));
    }
""", {"selector": "textarea[name='description']", "value": bio_text})

コード/設定の抜粋

CDP接続の基本構成。Braveを --remote-debugging-port=9222 で起動済み前提。

from playwright.async_api import async_playwright

async def connect_brave():
    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp("http://localhost:9222")
        context = browser.contexts[0]
        page = await context.new_page()
        return browser, page

定期実行を launchd に乗せる場合(環境パスは各自調整):

<key>ProgramArguments</key>
<array>
    <string>/usr/local/bin/python3</string>
    <string>/path/to/wantedly_profile.py</string>
</array>
<key>EnvironmentVariables</key>
<dict>
    <key>PATH</key>
    <string>/usr/local/bin:/usr/bin:/bin</string>
</dict>

試してわかったこと

Wantedlyは新規アカウントの場合、プロフィール編集ページへの直接 goto が機能しない。「ユーザーIDが確定していないとプロフィールURLが存在しない」という設計で、ウィザード未完了なら404が返ってくる。言われてみれば当然なんだけど、スクリプト側はその前提を完全に無視していた。

同じパターンはCoconaraでも出た。業務領域モーダルのフローがカテゴリによって「直接保存」と「次へ→追加」に分かれていて、想定外のルートに入って詰まった。サービスが「アカウントの状態に応じて画面フローを変える」設計になっていると、URLハードコードは弱い。

「現在地を確認してから次の手を決める」——goto 後に page.url を見るだけでかなり変わった。どのWebサービス自動化にも使える基本構造だと思う。

ReactアプリのnativeセッターパターンはCoconaraで実証済みで、今回もそのまま流用できた。「一度解決した詰まりは再利用できる」のが自動化ライブラリを蓄積していく理由だと改めて感じた。

まとめ

新規Wantedlyアカウントは、プロフィール編集URLに直接 goto するより先にID設定ウィザードがある。
遷移後のURLを確認してウィザードを検出・通過する分岐を1つ入れるだけで動いた。
「今どこにいるか」を確認してから動くスクリプトは、アカウント状態に左右されずに壊れにくい。✨