CDP 浏览器操控原理:让脚本接管你的浏览器

CDP 浏览器操控原理:让脚本接管你的浏览器

上一篇讲了 WorkBuddy Skill 的基本概念,这篇我们深入技术核心------如何用 Chrome DevTools Protocol + Playwright 实现真正的浏览器自动化。文中的所有代码都来自一个实际跑通的自动化项目(每天自动浏览 96 页课程、批改 62 份作业)。


1. 为什么不用 Selenium

做过 Web 自动化的同学第一反应通常是 Selenium。但它有几个致命问题:

问题 Selenium CDP + Playwright
被检测 WebDriver 特征明显 直接连真实浏览器,无特征
登录态 需要写登录流程 复用已有登录态
验证码 每次都要过 手动登录一次,Cookie 复用
维护成本 登录流程一变就挂 不受登录流程影响

核心区别:Selenium 是"开一个受控的新浏览器",CDP 是"接管你已经登录好的浏览器"。

对于需要短信验证码双因子认证的平台,CDP 几乎是唯一可行的方案。


2. CDP 协议简述

Chrome DevTools Protocol 是 Chromium 内核浏览器暴露的调试协议。你按 F12 打开开发者工具时,浏览器内部走的就是 CDP。

CDP 的通信基于 WebSocket,默认调试端口是 9222。连接后你可以做几乎任何事:

  • 导航到指定 URL
  • 执行 JavaScript(包括获取/修改 Angular scope)
  • 获取 DOM 节点、点击、输入
  • 监听网络请求
  • 截图

Playwright 封装了 CDP 协议,提供了更友好的 Python API。


3. 启动浏览器调试模式

Windows(Edge)

batch 复制代码
:: start_edge_debug.bat
taskkill /F /IM msedge.exe 2>nul
start msedge.exe ^
  --remote-debugging-port=9222 ^
  --user-data-dir="C:\Users\%USERNAME%\edge-debug-profile" ^
  --no-first-run ^
  --no-default-browser-check

关键参数

参数 作用
--remote-debugging-port=9222 暴露 CDP 调试端口
--user-data-dir=... 独立用户目录,不会干扰你日常用的浏览器
--no-first-run 跳过首次运行向导

⚠️ 必须先启动调试浏览器,再运行 Python 脚本。


4. Playwright 连接调试浏览器

python 复制代码
from playwright.sync_api import sync_playwright

CDP_URL = "http://localhost:9222"

def connect_browser():
    """连接到已打开的调试 Edge"""
    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp(CDP_URL)
        context = browser.contexts[0]  # 使用已有上下文
        page = context.pages[0]        # 使用已有标签页
        return p, browser, context, page

连接后,page 对象就是你在调试浏览器里看到的那个标签页。


这是自动化工程中最关键的一环:

scss 复制代码
                    首次运行
                       ↓
              [没有 Cookie 文件]
                       ↓
              需要用户手动登录
              (含短信验证码)
                       ↓
              脚本检测登录成功
                       ↓
        save_storage_state() → cookies.json
                       ↓
            ┌──────────────────────────┐
            │    后续每次运行            │
            │  load cookies.json       │
            │       ↓                  │
            │  访问平台首页             │
            │   ├─ 正常 → 继续执行      │
            │   └─ 重定向 → Cookie失效  │
            │              ↓           │
            │        等用户手动登录      │
            └──────────────────────────┘
python 复制代码
import json

def save_cookies(context, filepath="cookies.json"):
    state = context.storage_state()
    with open(filepath, "w", encoding="utf-8") as f:
        json.dump(state, f, ensure_ascii=False)

加载与检测

python 复制代码
def load_cookies(browser, filepath="cookies.json"):
    if not os.path.exists(filepath):
        return None, None
    with open(filepath, "r") as f:
        state = json.load(f)
    context = browser.new_context(storage_state=state)
    return context, context.new_page()

def check_login(page, platform_url):
    page.goto(platform_url, wait_until="domcontentloaded")
    page.wait_for_timeout(3000)
    if "login" in page.url.lower():
        return False
    return True

实际经验:某教育平台的 Cookie 约 7-10 天过期。设好失效检测,别让脚本静默失败。


6. Angular 页面操控进阶

现代 Web 应用大量使用 Angular/React/Vue,直接改 DOM 的值往往无效------框架有自己的状态管理。

问题场景

你看到一个 <select> 元素,用 Playwright 选了选项,但 Angular 控制器不知道,提交时还是旧值。

解决方案:Scope 注入

javascript 复制代码
// 获取 Angular scope
const scope = angular.element(
  document.querySelector('[ng-controller]')
).scope();

// 修改数据
scope.conditions.role = ['assistant_instructor'];

// 通知 Angular 更新
scope.$apply();

Python 侧封装

python 复制代码
def set_angular_scope(page, selector, expression):
    return page.evaluate(f"""
        (() => {{
            const el = document.querySelector('{selector}');
            if (!el) return 'not found';
            const scope = angular.element(el).scope();
            {expression}
            scope.$apply();
            return 'ok';
        }})()
    """)

实战:触发隐藏弹窗

python 复制代码
def trigger_modal(page):
    page.evaluate("""
        (() => {
            const modal = document.querySelector('#give-score');
            if (modal) {
                modal.style.display = 'block';
                modal.style.visibility = 'visible';
            }
        })()
    """)

7. Select2 / MultiSelect 组件处理

这是自动化中最容易翻车的地方。jQuery UI MultiSelect 或 Select2 组件不能通过简单设 value 来选值

❌ 错误做法

python 复制代码
page.select_option("select.course-role", "assistant_instructor")

✅ 正确做法

python 复制代码
page.evaluate("""
    (() => {
        const scope = angular.element(
            document.querySelector('[ng-controller="CourseListController"]')
        ).scope();
        scope.conditions.role = ['assistant_instructor'];
        scope.search();  // 触发后端筛选
    })()
""")

核心原则:找框架组件的控制器 scope,改 scope 上的值,调 scope 的方法------不碰 DOM。


8. 完整连接流程模板

python 复制代码
import json
from playwright.sync_api import sync_playwright

CDP_URL = "http://localhost:9222"
PLATFORM_URL = "https://your-platform.com"

def main():
    with sync_playwright() as p:
        # 1. 连接调试浏览器
        browser = p.chromium.connect_over_cdp(CDP_URL)
        
        # 2. 加载 Cookie
        context, page = load_cookies(browser)
        if not context:
            context = browser.contexts[0]
            page = context.pages[0]
        
        # 3. 检测登录态
        if not check_login(page, PLATFORM_URL):
            print("请在浏览器中手动登录,完成后按回车...")
            input()
            save_cookies(context)
        
        # 4. 执行任务
        do_tasks(page)
        
        # 5. 保存 Cookie
        save_cookies(context)

9. 常见踩坑

现象 解决
连不上 9222 Connection refused 确认调试浏览器已启动
页面空白 内容不可见 Angular 初始化慢,等 10 秒以上
scope undefined 取不到 scope 确认元素有 ng-controller,等待足够久
Cookie 写入失败 登录后仍需重登 storage_state() 需包含 localStorage
端口占用 启动失败 taskkill /F /IM msedge.exe

10. 小结

学会的 要点
CDP vs Selenium CDP 复用已登录态,绕过验证码
启动调试浏览器 --remote-debugging-port=9222 + 独立 user-data-dir
Playwright 连接 connect_over_cdp()
Cookie 管理 storage_state() 保存/加载 + 失效检测
Angular 操控 scope 注入 + $apply(),别直接改 DOM
Select2 组件 走 scope 不走 DOM

下一篇预告

第 3 节:登录态持久化与双因子认证------Cookie 生命周期管理、失败自动恢复、多终端同步策略。敬请关注!


上一篇:第1节:WorkBuddy Skill 入门

本文首发于稀土掘金,作者实战运行自动化脚本超百次,代码全部可运行。欢迎关注后续连载。

相关推荐
ThreeS1 小时前
手搓MiniVLA全实战教程-一步一步用pytorch解释原理与思路
人工智能·python
米小虾2 小时前
Loop Engineering —— 循环的设计与自主执行
人工智能·agent
米小虾2 小时前
Harness Engineering —— 系统的安全护栏
人工智能·agent
火山引擎开发者社区2 小时前
积分当钱花,火山引擎开发者激励计划首月消费双倍回馈
人工智能
aqi003 小时前
15天学会AI应用开发(十)把文本嵌入模型换成国产模型
人工智能·python·ai编程
MobotStone3 小时前
为什么在AI时代,“好奇心”成了最值钱的能力?
人工智能
武子康4 小时前
调查研究-200 llama.cpp b9754:一次很小但很关键的 Agent 工具调用修复
人工智能·agent·llama