<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>RPA on 扎塔-Zata</title><link>https://www.zata.cc/tags/rpa/</link><description>Recent content in RPA on 扎塔-Zata</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>Example Person</copyright><lastBuildDate>Tue, 28 Jul 2026 09:14:59 +0800</lastBuildDate><atom:link href="https://www.zata.cc/tags/rpa/index.xml" rel="self" type="application/rss+xml"/><item><title>扩展式 RPA：从 Chrome 扩展原理到 Playwright 实战方案</title><link>https://www.zata.cc/p/%E6%89%A9%E5%B1%95%E5%BC%8Frpa%E4%BB%8Echrome%E6%89%A9%E5%B1%95%E6%8E%A5%E7%AE%A1%E5%88%B0playwright%E5%AE%9E%E6%88%98/</link><pubDate>Mon, 27 Jul 2026 10:00:00 +0800</pubDate><guid>https://www.zata.cc/p/%E6%89%A9%E5%B1%95%E5%BC%8Frpa%E4%BB%8Echrome%E6%89%A9%E5%B1%95%E6%8E%A5%E7%AE%A1%E5%88%B0playwright%E5%AE%9E%E6%88%98/</guid><description>&lt;img src="https://www.zata.cc/p/%E6%89%A9%E5%B1%95%E5%BC%8Frpa%E4%BB%8Echrome%E6%89%A9%E5%B1%95%E6%8E%A5%E7%AE%A1%E5%88%B0playwright%E5%AE%9E%E6%88%98/images/index/index.svg" alt="Featured image of post 扩展式 RPA：从 Chrome 扩展原理到 Playwright 实战方案" />&lt;h2 id="一个观察">一个观察
&lt;/h2>&lt;p>之前在 [[../L../Library/Python_Lib/playwright/index.md]] 里写过 Playwright 自己起隔离浏览器的常规打法。但在看到一些 RPA 工具的形态后，问题就变成另一个：&lt;/p>
&lt;blockquote>
&lt;p>为什么有些 RPA 工具是装成 Chrome 扩展，一打开就能直接「操控」用户已经登录好的页面？&lt;/p>
&lt;/blockquote>
&lt;p>答案并不复杂：这两种工具在底层就是两套完全不同的思路。&lt;/p>
&lt;h2 id="核心原理谁拥有那个浏览器">核心原理：谁拥有那个浏览器
&lt;/h2>&lt;p>普通 Playwright(或者类似 Puppeteer、Selenium)启动的是&lt;strong>全新的、独立的浏览器实例&lt;/strong>，和你日常用的 Chrome 没有任何关系。Cookies 是空的，需要重新登录或注入 &lt;code>storage_state&lt;/code>。&lt;/p>
&lt;p>而 Chrome 扩展型 RPA 长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">┌──────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 你的 Chrome 浏览器（同进程） │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ┌────────────┐ ┌────────────────────┐ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ Tab: 微博 │ │ Extension: RPA 工具 │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ (已登录) │ │ 注入 content script │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └────────────┘ └────────────────────┘ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ↑ │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └────────────────┘ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 共享同一个 cookie 存储 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 共享同一个 origin 权限 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└──────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>扩展里跑的内容脚本 (&lt;code>content script&lt;/code>) 语法上是独立脚本，但它&lt;strong>直接被注入到登录页面的 DOM 里&lt;/strong>，运行在该页面的 JS context 中。所以当它调用 &lt;code>fetch('/api/user/info')&lt;/code> 时，浏览器自动带上微博的 cookies，服务器认为这就是用户本人。&lt;/p>
&lt;p>整件事可以浓缩成一句话：&lt;/p>
&lt;blockquote>
&lt;p>扩展是装在你浏览器里的代码，你在浏览器里登录了什么，它就拥有什么。&lt;/p>
&lt;/blockquote>
&lt;p>这也是为什么做需要登录态的 RPA 任务，扩展方案往往比 Playwright 简单十倍 —— 因为它根本不需要解决「登录」这个问题。&lt;/p>
&lt;h3 id="几块关键拼图">几块关键拼图
&lt;/h3>&lt;p>扩展形式看起来神秘，其实就是下面这几块常规 MV3 API 拼起来的：&lt;/p>
&lt;p>&lt;code>manifest.json&lt;/code> 声明权限：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;permissions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;cookies&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;storage&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tabs&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;scripting&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;activeTab&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;host_permissions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;*://*.weibo.com/*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;*://*.example.com/*&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>content.js&lt;/code> 注入目标页面，所有 &lt;code>fetch&lt;/code> 自动带登录态：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">autoPost&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">content&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">querySelector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;#editor&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">querySelector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;#submit&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">click&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">chrome&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">runtime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onMessage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addListener&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">msg&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">sender&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">sendResponse&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">msg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cmd&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;post&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoPost&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">msg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">text&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sendResponse&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">ok&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>后台脚本 &lt;code>background.js&lt;/code> 里直接读 cookie：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">chrome&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cookies&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getAll&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">domain&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;.weibo.com&amp;#39;&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">cookies&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">cookies&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 直接拿到用户的所有登录态
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>把上面三块加一个 &lt;code>popup.html&lt;/code>，就是最简版「扩展式 RPA」。&lt;/p>
&lt;h3 id="两种方案对比">两种方案对比
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>维度&lt;/th>
&lt;th>Playwright / 独立 RPA&lt;/th>
&lt;th>扩展式 RPA&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>启动方式&lt;/td>
&lt;td>独立进程，自己的浏览器&lt;/td>
&lt;td>跑在用户已有的 Chrome&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>登录态&lt;/td>
&lt;td>无，需要自登录或注入 &lt;code>storage_state&lt;/code>&lt;/td>
&lt;td>&lt;strong>天然继承&lt;/strong>用户浏览器的登录态&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>反爬检测&lt;/td>
&lt;td>高（指纹、IP、行为模式）&lt;/td>
&lt;td>低（就是用户本人）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>用户授权&lt;/td>
&lt;td>无感（代码里随便用）&lt;/td>
&lt;td>需要用户主动装扩展&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>部署难度&lt;/td>
&lt;td>简单，纯代码&lt;/td>
&lt;td>需要打包扩展、上架商店或 crx 安装&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>资源占用&lt;/td>
&lt;td>完整浏览器进程&lt;/td>
&lt;td>几乎为零&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>适用场景&lt;/td>
&lt;td>批量任务、服务器端&lt;/td>
&lt;td>辅助个人用户、低频任务&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="反爬角度的思考">反爬角度的思考
&lt;/h3>&lt;p>扩展式 RPA 看起来「开挂」，但也不是万能：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>指纹检测&lt;/strong>：&lt;code>window.chrome.runtime&lt;/code> 等扩展特征可以被检测&lt;/li>
&lt;li>&lt;strong>行为检测&lt;/strong>：点击速度、操作序列如果太机械，仍能被识别&lt;/li>
&lt;li>&lt;strong>数据回流&lt;/strong>：云端 SaaS 形态的 RPA 在网络层会被分析&lt;/li>
&lt;li>&lt;strong>扩展权限&lt;/strong>：Chrome 在安装时展示权限，敏感权限可能让用户警觉&lt;/li>
&lt;li>&lt;strong>Manifest V3 限制&lt;/strong>：MV3 收紧了远程代码执行、跨域请求，扩展型 RPA 的能力被削弱&lt;/li>
&lt;/ol>
&lt;h3 id="商业产品参考">商业产品参考
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>UI.Vision (Kantu)&lt;/strong>：经典老牌，扩展形式，直接读用户登录态&lt;/li>
&lt;li>&lt;strong>Browserflow&lt;/strong>：Chrome 商店里很常见&lt;/li>
&lt;li>&lt;strong>Axiom.ai&lt;/strong>：扩展 + 云端&lt;/li>
&lt;li>&lt;strong>影刀 / 八爪鱼&lt;/strong>：部分功能走扩展模式，处理需要登录的网站&lt;/li>
&lt;li>&lt;strong>Automa&lt;/strong>（开源）：GitHub 上有源码，可以学到完整的实现&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="用-playwright-模拟扩展式-rpa">用 Playwright 模拟扩展式 RPA
&lt;/h2>&lt;p>虽然「真扩展」的形态在 Playwright 这边不好复刻（Playwright 是独立进程，无法直接读取用户 Chrome 的 cookies），但有一种折中方案：让 Playwright &lt;strong>复用用户已登录的浏览器&lt;/strong>。技术上叫 &lt;strong>Persistent Context&lt;/strong>。&lt;/p>
&lt;p>核心 API 是 &lt;code>launch_persistent_context&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">playwright.sync_api&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">sync_playwright&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">sync_playwright&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 关键：指向真实 Chrome 的用户数据目录&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># macOS : ~/Library/Application Support/Google/Chrome&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Windows: %LOCALAPPDATA%\Google\Chrome\User Data&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chromium&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">launch_persistent_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_data_dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;/Users/zata/Library/Application Support/Google/Chrome&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">channel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;chrome&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 强制用系统 Chrome，不是 chromium&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headless&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 必须可视化（见下面）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">no_viewport&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;--disable-blink-features=AutomationControlled&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;--no-first-run&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">new_page&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">goto&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https://weibo.com&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 此时你已经是登录状态了，直接用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">locator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;.follow-btn&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">click&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="但这条路的坑很大">但这条路的坑很大
&lt;/h3>&lt;p>和同仓库那篇 [[../L../Library/Python_Lib/playwright/index.md]] 的警告一致 —— 复用真实 Chrome 的 Profile 风险极高：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>必须先完全关闭 Chrome&lt;/strong>：用户数据目录是文件锁定的，Chrome 在开的时候 Playwright 启动会直接报 &lt;code>Failed to create a ProcessSingleton for your profile&lt;/code>。&lt;/li>
&lt;li>&lt;strong>&lt;code>headless=False&lt;/code> 是硬需求&lt;/strong>：不是「看得到」这么简单的诉求，而是 headless 模式下被检测概率会显著上升，自动化特征也更明显。&lt;/li>
&lt;li>&lt;strong>User Data 目录要用整个 &lt;code>User Data&lt;/code>，而不是 &lt;code>Default&lt;/code>&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>不要做 &lt;code>rm -rf&lt;/code> 这种清理动作&lt;/strong>：容易连带覆盖或损坏历史、书签。&lt;/li>
&lt;/ol>
&lt;p>写个辅助函数降低出错的可能：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">subprocess&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">kill_chrome&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;杀进程,确保数据目录不被 Chrome 自己锁住&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;pkill&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;-9&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Google Chrome&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">check&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="实际可运行的完整脚本">实际可运行的完整脚本
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># rpa.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">subprocess&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pathlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Path&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">playwright.sync_api&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">sync_playwright&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CHROME_PATH&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/Applications/Google Chrome.app/Contents/MacOS/Google Chrome&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">USER_DATA&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">home&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="s1">&amp;#39;Library/Application Support/Google/Chrome&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">ensure_chrome_closed&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;pkill&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;-9&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;-f&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Google Chrome&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">check&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ensure_chrome_closed&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="n">sync_playwright&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Playwright 启动一个临时 Chrome 进程,加载你的用户数据&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chromium&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">launch_persistent_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_data_dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">USER_DATA&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">executable_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">CHROME_PATH&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headless&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">no_viewport&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;--disable-blink-features=AutomationControlled&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ignore_default_args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;--enable-automation&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">browser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pages&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">browser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pages&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="n">browser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">new_page&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">goto&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https://weibo.com&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 验证:这里应该能直接看到登录态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="s1">&amp;#39;登录&amp;#39;&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">page&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="s1">&amp;#39;用户态异常,可能 Chrome 未正常启动&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 开始自动化...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">goto&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https://weibo.com/u/1234567890&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">locator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;text=关注&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">click&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;✅ 关注成功&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">finally&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">run&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="进阶cdp-远程连接">进阶：CDP 远程连接
&lt;/h3>&lt;p>&lt;code>launch_persistent_context&lt;/code> 有一个尴尬的体验：用户在自己用 Chrome，你想跑 RPA，必须先杀 Chrome 再启动一次。更优雅的方案是 &lt;strong>Chrome DevTools Protocol（CDP）远程连接&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 第一步：用户在终端手动启动 Chrome（带远程调试）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">google-chrome --remote-debugging-port&lt;span class="o">=&lt;/span>&lt;span class="m">9222&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">playwright.sync_api&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">sync_playwright&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">sync_playwright&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 第二步：Playwright 接管这个 Chrome,而不是新启&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chromium&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">connect_over_cdp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;http://localhost:9222&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">browser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">contexts&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 复用用户的 context&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pages&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 接管用户当前的 tab&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">goto&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https://weibo.com&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>CDP 方案的好处：&lt;/p>
&lt;ul>
&lt;li>不需要杀 Chrome&lt;/li>
&lt;li>用户当前打开的 tab 可以直接被我们接管&lt;/li>
&lt;li>跑完可以「无缝」还给用户&lt;/li>
&lt;/ul>
&lt;h3 id="playwright--chrome-extension">Playwright + Chrome Extension
&lt;/h3>&lt;p>如果目标是&lt;strong>让用户装一个扩展，由扩展来驱动云端 Playwright&lt;/strong>，逻辑反过来：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 用户 Chrome │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── 你的 Extension (MV3) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └── WebSocket 客户端 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── 普通网页 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── 被扩展的 content script 操控 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└────────────┬────────────────────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ ws://localhost:8765
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 你的 Python 服务 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── WebSocket Server │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── Playwright(可选,云端兜底) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这种架构适合做 &lt;strong>SaaS 化的 RPA&lt;/strong>：扩展做「采集 + 可视化」，云端 Playwright 做「批量执行」。&lt;/p>
&lt;hr>
&lt;h2 id="方案选型表">方案选型表
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>推荐方案&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>个人脚本，偶尔跑一下&lt;/td>
&lt;td>&lt;code>launch_persistent_context&lt;/code> + 杀 Chrome&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>个人脚本，经常跑&lt;/td>
&lt;td>CDP 远程连接 + 一个一键启动 Chrome 的 &lt;code>.command&lt;/code> 文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具化，要给同事用&lt;/td>
&lt;td>CDP 方案 + 配启动器&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>做产品，要分发&lt;/td>
&lt;td>写 Chrome 扩展（完全独立，无需 Playwright）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>前三种方案都没跳出 Playwright 的能力范围，基本能覆盖 90% 的需求。最后一种是真正的「扩展式 RPA」，但投入就大很多了 —— 它本身就是一个独立的产品形态，从 manifest、签名、商店审核到 MV3 限制都是另一套工程。&lt;/p>
&lt;p>如果是从零开始想把 RPA 能力给到个人用户，&lt;strong>先从 CDP 方案做起&lt;/strong>，把脚本跑通、把页面录制做成、把回放做成；等到扩展的能力真的有必要了，再考虑写 MV3 分发。&lt;/p>
&lt;h2 id="参考资料">参考资料
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch-persistent-context" target="_blank" rel="noopener"
>Playwright: &lt;code>launch_persistent_context&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://playwright.dev/python/docs/other-ports#connect-over-cdp" target="_blank" rel="noopener"
>Playwright: Connect over CDP&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3" target="_blank" rel="noopener"
>Chrome Extensions: Manifest V3 Migration&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://developer.chrome.com/docs/extensions/reference/api/cookies" target="_blank" rel="noopener"
>Chrome Extensions: Cookies API&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/rrweb-io/rrweb" target="_blank" rel="noopener"
>rrweb / rrweb-player&lt;/a>（会话录制的开源参考实现）&lt;/li>
&lt;li>&lt;a class="link" href="https://www.automa.site/" target="_blank" rel="noopener"
>Automa - Chrome 扩展式 RPA 开源实现&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>