Skip to content

快速開始:接管候鳥環境

本章節提供三種語言的完整、可運行腳本。請選擇您最熟悉的語言進行操作。核心邏輯完全一致:向 ApiServer 請求 ws 地址,然後使用 connectOverCDP 接管候鳥。


方式一:JavaScript / Node.js 版

創建文件 start.js:

javascript
const { chromium } = require('playwright');
const axios = require('axios');

// ============================
// 配置項 (請填寫您的實際信息)
// ============================
const API_URL    = 'http://127.0.0.1:8186';
const SESSION_ID = 'replace_with_your_session_id';   // 候鳥環境右鍵 -> 複製環境 ID

async function main() {
  // ── 步驟 1: 告訴 ApiServer 開啟指定環境 ────────
  console.log('⏳ 正在開啟候鳥環境...');
  let wsEndpoint;
  try {
    const response = await axios.post(`${API_URL}/api/v1/browser/start`, {
      Session_ID: SESSION_ID
    });
    if (response.data.code !== 0) {
      console.error('❌ 環境啟動失敗:', response.data.message);
      return;
    }
    // ── 步驟 2: 提取 WebSocket 地址 (Playwright 專用字段) ──
    wsEndpoint = response.data.data.ws;
    console.log('✅ 環境已開啟。ws 地址:', wsEndpoint);
  } catch (err) {
    console.error('❌ 無法連接 ApiServer,請確保它已運行!', err.message);
    return;
  }

  // ── 步驟 3: 使用 connectOverCDP 接管 ─────────────────
  const browser = await chromium.connectOverCDP(wsEndpoint);
  console.log('🎉 Playwright 接管成功!');

  try {
    // ── 步驟 4: 獲取當前上下文與頁面 ────────────────────
    // 候鳥環境有默認上下文,直接取第一個
    const context = browser.contexts()[0];
    const page    = context.pages()[0] || await context.newPage();

    // ── 步驟 5: 執行業務操作 ─────────────────────
    await page.goto('https://www.mbbrowser.com', { waitUntil: 'domcontentloaded' });
    console.log('📄 當前頁面標題:', await page.title());

    // 等待 3 秒方便觀察
    await page.waitForTimeout(3000);

    console.log('✅ 腳本執行結束!');
  } finally {
    // 斷開 Playwright 連接 (候鳥窗口會繼續運行)
    await browser.close();
  }
}

main().catch(console.error);

運行:

bash
node start.js

方式二:Python 版

創建文件 start_playwright.py:

python
import requests
from playwright.sync_api import sync_playwright

# ============================
# 配置項
# ============================
API_URL    = "http://127.0.0.1:8186"
SESSION_ID = "replace_with_your_session_id"


def get_ws_endpoint(session_id: str) -> str or None:
    """調用候鳥 ApiServer 啟動環境並返回 ws 地址"""
    print("⏳ 正在開啟候鳥環境...")
    try:
        resp = requests.post(
            f"{API_URL}/api/v1/browser/start",
            json={"Session_ID": session_id},
            timeout=30
        )
        data = resp.json()
    except requests.exceptions.ConnectionError:
        print("❌ 無法連接 ApiServer!請確保 apiserver.exe 已運行。")
        return None

    if data.get("code") != 0:
        print(f"❌ 環境啟動失敗: {data.get('message')}")
        return None

    ws = data["data"]["ws"]
    print(f"✅ 環境已開啟。ws 地址: {ws}")
    return ws


def main():
    ws_endpoint = get_ws_endpoint(SESSION_ID)
    if not ws_endpoint:
        return

    with sync_playwright() as p:
        # ── 使用 connect_over_cdp 接管 ─────────────────
        browser = p.chromium.connect_over_cdp(ws_endpoint)
        print("🎉 Playwright 接管成功!")

        # 取出已有的默認 BrowserContext (保留所有 Cookie, Storage 等)
        context = browser.contexts[0]
        # 取首個頁面,若無則建新頁
        page = context.pages[0] if context.pages else context.new_page()

        # ── 執行業務操作 ──────────────────────────────
        page.goto("https://www.mbbrowser.com", wait_until="domcontentloaded")
        print(f"📄 當前頁面標題: {page.title()}")

        page.wait_for_timeout(3000)
        print("✅ 腳本執行結束!")

        browser.close()


if __name__ == "__main__":
    main()

運行:

bash
python start_playwright.py

方式三:Java 版

NOTE

對於 Java 版本,我們推薦使用候鳥客戶端的自動化腳本管理器,它會自動處理 Playwright JAR 包和驅動提取。以下代碼可直接在腳本編輯器中使用。

java
import com.microsoft.playwright.*;
import java.net.URI;
import java.net.http.*;
import org.json.*;

public class MBPlaywright {
    static final String API_URL    = "http://127.0.0.1:8186";
    static final String SESSION_ID = "replace_with_your_session_id";

    public static void main(String[] args) throws Exception {
        // ── 步驟 1: 調用 ApiServer 啟動環境 ──────────────
        System.out.println("⏳ 正在開啟候鳥環境...");
        HttpClient httpClient = HttpClient.newHttpClient();
        String body = "{\"Session_ID\":\"" + SESSION_ID + "\"}";
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(API_URL + "/api/v1/browser/start"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();
        HttpResponse<String> response = httpClient.send(
            request, HttpResponse.BodyHandlers.ofString()
        );

        JSONObject data   = new JSONObject(response.body());
        String wsEndpoint = data.getJSONObject("data").getString("ws");
        System.out.println("✅ 環境已開啟。ws: " + wsEndpoint);

        // ── 步驟 2: Playwright connectOverCDP 接管 ───────────────
        // 設置跳過內置瀏覽器下載
        System.setProperty("playwright.skip.browser.download", "1");

        try (Playwright playwright = Playwright.create()) {
            Browser browser = playwright.chromium().connectOverCDP(wsEndpoint);
            System.out.println("🎉 Playwright 接管成功!");

            BrowserContext context = browser.contexts().get(0);
            Page page = context.pages().isEmpty()
                ? context.newPage()
                : context.pages().get(0);

            page.navigate("https://www.mbbrowser.com");
            System.out.println("📄 當前頁面標題: " + page.title());

            Thread.sleep(3000);
            System.out.println("✅ 腳本執行結束!");

            browser.close();
        }
    }
}

逐行解析

connectOverCDP vs launch: 決定性區別

javascript
// ❌ 普通 Playwright 做法:新建一個空白的 Chrome 窗口
const browser = await chromium.launch();

// ✅ 候鳥 Playwright 做法:接管已有的指紋環境
const browser = await chromium.connectOverCDP(wsEndpoint);
對比項普通 launch()候鳥 connectOverCDP()
瀏覽器狀態全新、空白帶指紋、代理、已有 Cookie
後果目標站點識別為 bare Chrome bot目標站點識別為「真人用戶」
多帳號無法區分身份通過 Session_ID 精確對應

為什麼用 data.ws 而不是 data.http

字段示例格式適用框架
data.http127.0.0.1:9222Selenium (debuggerAddress)
data.wsws://127.0.0.1:9222/devtools/browser/xxxPlaywright / Puppeteer

Playwright 的 connectOverCDP 必須使用完整的 ws://... 地址。

browser.contexts()[0]: 不要創建新上下文

javascript
// ❌ 錯誤:新建上下文,候鳥中的 Cookie、存儲將丟失。
const context = await browser.newContext();

// ✅ 正確:使用候鳥已有的上下文 (保留帳號狀態)。
const context = browser.contexts()[0];

常見問題故障排除

❓ 報錯: connect ECONNREFUSED 127.0.0.1:8186

ApiServer 未啟動或端口填寫錯誤。請確保 apiserver.exe 在終端窗口中運行。

❓ 報錯: Browser closedTarget page, context or browser has been closed

候鳥環境已被關閉 (超時或人工關閉)。請重新調用 /api/v1/browser/start

browser.contexts() 返回空數組

環境剛開啟時,默認上下文初始化可能需要幾百毫秒。建議加入短暫重試機制:

javascript
// 等待上下文出現 (最多等 5 秒)
let context;
for (let i = 0; i < 50 && !browser.contexts().length; i++) {
  await new Promise(r => setTimeout(r, 100));
}
context = browser.contexts()[0];

browser.close() 會關閉候鳥窗口嗎?

connectOverCDP 模式下,browser.close() 僅會斷開 Playwright 的鏈接,不會關閉候鳥窗口。如需關閉窗口,請調用 stop 接口:

javascript
await axios.post(`${API_URL}/api/v1/browser/stop`, { Session_ID: SESSION_ID });

TIP

準備好碼代碼了嗎?下一章 核心 API 參考 將教您如何使用 Playwright 強大な Locator 和 Page API。