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 入门

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

相关推荐
@insist12334 分钟前
系统规划与管理师-流程评价与持续改进核心知识-终章
大数据·人工智能·软考·系统规划与管理师·软件水平考试·系统规划与管理工程师
meilindehuzi_a36 分钟前
Workflow 与 Agent 有什么区别:从 LangChain 流水线到智能体决策
前端·人工智能
SLD_Allen38 分钟前
大规模分布式AI训练基础设施
人工智能·分布式·模型训练
码农学院1 小时前
AI优化AIO技术演进史:从模板生成到多智能体协同创作
人工智能
科技发布1 小时前
拓氪科技 AI 内容引擎软文投放效果详解
人工智能·科技
nanawinona1 小时前
2026年下半年量化学习,不同基础要查不同缺口
人工智能·python
海盗12341 小时前
AI新闻日报_2026-07-22
人工智能
CTA量化套保1 小时前
最新量化表达入门,从概念规则到简单实现
人工智能·python
AI应用苏大大1 小时前
企业AI架构缺陷:低价工具堆叠,造成结构性效率浪费
人工智能
空中湖2 小时前
Spring AI Agent 编排:ReAct 模式 + 多 Agent 协作实战
人工智能·spring·react.js