<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog</id>
    <title>DeepSeek API 文档(Sao) Blog</title>
    <updated>2026-10-25T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog"/>
    <subtitle>DeepSeek API 文档(Sao) Blog</subtitle>
    <icon>https://wxhzhwxhzh.github.io/deepseek-helper-docs/img/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[Pi Agent 常用热键与斜杠命令速查]]></title>
        <id>https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands</id>
        <link href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands"/>
        <updated>2026-10-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Pi Agent 的交互几乎全靠两样东西：热键和斜杠命令。前者管输入、编辑、导航和显示，后者管会话、模型和配置。很多人只用过 /model 和 Ctrl+C，其实内置命令有 24 个、可绑定动作有上百个。]]></summary>
        <content type="html"><![CDATA[<p>Pi Agent 的交互几乎全靠两样东西：<strong>热键</strong>和<strong>斜杠命令</strong>。前者管输入、编辑、导航和显示，后者管会话、模型和配置。很多人只用过 <code>/model</code> 和 <code>Ctrl+C</code>，其实内置命令有 24 个、可绑定动作有上百个。</p>
<p>本文按「日常最常用 → 场景专用」的顺序整理，全部基于 pi 1.4.0 的实际命令注册表和默认键位。所有默认热键都能改，方式见文末。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一最该先记住的-7-个热键">一、最该先记住的 7 个热键<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E4%B8%80%E6%9C%80%E8%AF%A5%E5%85%88%E8%AE%B0%E4%BD%8F%E7%9A%84-7-%E4%B8%AA%E7%83%AD%E9%94%AE" class="hash-link" aria-label="一、最该先记住的 7 个热键的直接链接" title="一、最该先记住的 7 个热键的直接链接" translate="no">​</a></h2>
<p>如果只记七个，记这些：</p>
<table><thead><tr><th>热键</th><th>作用</th><th>说明</th></tr></thead><tbody><tr><td><code>Ctrl+C</code></td><td>清空输入框 / 再按退出</td><td>第一次清空编辑器内容，编辑器已空时再按就退出</td></tr><tr><td><code>Esc</code></td><td>中断当前操作</td><td>取消运行、关闭选择器</td></tr><tr><td><code>Ctrl+D</code></td><td>退出</td><td>仅在编辑器为空时生效</td></tr><tr><td><code>Shift+Tab</code></td><td>循环思考等级</td><td>off → minimal → low → medium → high → xhigh → max</td></tr><tr><td><code>Ctrl+L</code></td><td>打开模型选择器</td><td>比输入 <code>/model</code> 更快</td></tr><tr><td><code>Ctrl+P</code></td><td>切换到下一个模型</td><td>快速对比不同模型输出</td></tr><tr><td><code>Ctrl+O</code></td><td>折叠 / 展开工具输出</td><td>输出太长时先收起来</td></tr></tbody></table>
<p>记住这七个别小看：日常交互 80% 的操作都在里面。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二输入与编辑">二、输入与编辑<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E4%BA%8C%E8%BE%93%E5%85%A5%E4%B8%8E%E7%BC%96%E8%BE%91" class="hash-link" aria-label="二、输入与编辑的直接链接" title="二、输入与编辑的直接链接" translate="no">​</a></h2>
<p>Pi 的编辑器是类 Emacs 的键位，习惯之后手不用离开主键区。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-提交与换行">2.1 提交与换行<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#21-%E6%8F%90%E4%BA%A4%E4%B8%8E%E6%8D%A2%E8%A1%8C" class="hash-link" aria-label="2.1 提交与换行的直接链接" title="2.1 提交与换行的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>热键</th><th>作用</th></tr></thead><tbody><tr><td><code>Enter</code></td><td>提交输入</td></tr><tr><td><code>Shift+Enter</code> 或 <code>Ctrl+J</code></td><td>插入换行（多行输入）</td></tr><tr><td><code>Tab</code></td><td>补全 / 切换焦点</td></tr></tbody></table>
<p>多行输入是新手最容易卡住的点：直接按 <code>Enter</code> 就发出去了，长提示词要拆成好几轮。<strong>用 <code>Shift+Enter</code></strong>。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-光标移动">2.2 光标移动<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#22-%E5%85%89%E6%A0%87%E7%A7%BB%E5%8A%A8" class="hash-link" aria-label="2.2 光标移动的直接链接" title="2.2 光标移动的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>热键</th><th>作用</th></tr></thead><tbody><tr><td><code>←</code> / <code>→</code></td><td>左右移动一个字符</td></tr><tr><td><code>Ctrl+B</code> / <code>Ctrl+F</code></td><td>同上（Emacs 风格）</td></tr><tr><td><code>Alt+←</code> / <code>Alt+B</code></td><td>向左移动一个单词</td></tr><tr><td><code>Alt+→</code> / <code>Alt+F</code></td><td>向右移动一个单词</td></tr><tr><td><code>Home</code> / <code>Ctrl+A</code></td><td>移到行首</td></tr><tr><td><code>End</code> / <code>Ctrl+E</code></td><td>移到行尾</td></tr><tr><td><code>Ctrl+]</code></td><td>向前跳转到指定字符</td></tr><tr><td><code>Ctrl+Alt+]</code></td><td>向后跳转到指定字符</td></tr></tbody></table>
<p><code>Alt+←</code> / <code>Alt+→</code> 按单词跳，改长句子时效率远高于按住方向键。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-删除与撤销">2.3 删除与撤销<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#23-%E5%88%A0%E9%99%A4%E4%B8%8E%E6%92%A4%E9%94%80" class="hash-link" aria-label="2.3 删除与撤销的直接链接" title="2.3 删除与撤销的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>热键</th><th>作用</th></tr></thead><tbody><tr><td><code>Backspace</code></td><td>删除前一个字符</td></tr><tr><td><code>Delete</code> / <code>Ctrl+D</code></td><td>删除后一个字符</td></tr><tr><td><code>Ctrl+W</code> / <code>Alt+Backspace</code></td><td>向前删一个单词</td></tr><tr><td><code>Alt+D</code> / <code>Alt+Delete</code></td><td>向后删一个单词</td></tr><tr><td><code>Ctrl+U</code></td><td>删到行首</td></tr><tr><td><code>Ctrl+K</code></td><td>删到行尾</td></tr><tr><td><code>Ctrl+Y</code></td><td>粘贴刚删掉的内容</td></tr><tr><td><code>Alt+Y</code></td><td>在 <code>Ctrl+Y</code> 之后循环切换更早的删除内容</td></tr><tr><td><code>Ctrl+-</code></td><td>撤销（Windows 为 <code>Ctrl+Z</code>，WSL 为 <code>Alt+Z</code>）</td></tr></tbody></table>
<p><code>Ctrl+U</code> + <code>Ctrl+Y</code> 是很实用的组合：<strong>整行删掉但留住</strong>，改一半又想还原时不用重打。</p>
<p><code>Ctrl+W</code> 按单词删除，比连按 <code>Backspace</code> 快得多，而且配合 <code>Ctrl+U</code> 能快速重写半句话。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-历史与翻页">2.4 历史与翻页<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#24-%E5%8E%86%E5%8F%B2%E4%B8%8E%E7%BF%BB%E9%A1%B5" class="hash-link" aria-label="2.4 历史与翻页的直接链接" title="2.4 历史与翻页的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>热键</th><th>作用</th></tr></thead><tbody><tr><td><code>↑</code> / <code>↓</code></td><td>光标上下移动，在边界处浏览历史输入</td></tr><tr><td><code>PageUp</code> / <code>PageDown</code></td><td>按页滚动</td></tr></tbody></table>
<p>想专门翻历史而不是移动光标，可以绑定 <code>tui.editor.historyPrevious</code> / <code>historyNext</code>，默认没有绑定。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-剪贴板">2.5 剪贴板<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#25-%E5%89%AA%E8%B4%B4%E6%9D%BF" class="hash-link" aria-label="2.5 剪贴板的直接链接" title="2.5 剪贴板的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>热键</th><th>作用</th></tr></thead><tbody><tr><td><code>Ctrl+V</code></td><td>从剪贴板粘贴图片、文件或文本（Windows / WSL 为 <code>Alt+V</code>）</td></tr><tr><td><code>Ctrl+C</code></td><td>复制选中内容</td></tr><tr><td><code>Ctrl+G</code></td><td>用外部编辑器打开当前输入</td></tr></tbody></table>
<p><code>Ctrl+G</code> 值得单独说：长提示词或要粘贴大段代码时，调出 <code>$EDITOR</code>（Windows 下是记事本），写完保存关闭就回填到编辑器。比在小框里滚动舒服得多。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三会话与显示">三、会话与显示<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E4%B8%89%E4%BC%9A%E8%AF%9D%E4%B8%8E%E6%98%BE%E7%A4%BA" class="hash-link" aria-label="三、会话与显示的直接链接" title="三、会话与显示的直接链接" translate="no">​</a></h2>
<table><thead><tr><th>热键</th><th>作用</th></tr></thead><tbody><tr><td><code>Ctrl+T</code></td><td>折叠 / 展开思考块</td></tr><tr><td><code>Ctrl+O</code></td><td>折叠 / 展开工具输出</td></tr><tr><td><code>Ctrl+X</code></td><td>复制消息；OAuth 登录页可复制登录链接</td></tr><tr><td><code>Alt+Enter</code></td><td>排队发送后续消息（Windows / WSL 为 <code>Ctrl+Q</code>）</td></tr><tr><td><code>Alt+↑</code></td><td>把排队中的消息取回编辑器（Windows / WSL 为 <code>Alt+Q</code>）</td></tr><tr><td><code>Ctrl+Z</code></td><td>挂起到后台（Windows 无默认键位）</td></tr></tbody></table>
<p><strong>消息排队</strong>是个被低估的功能。模型还在跑的时候按 <code>Alt+Enter</code> 发下一条，它不会打断当前运行，而是在结束后自动接着处理。相当于"提前把话说完"。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四全屏模式专用">四、全屏模式专用<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E5%9B%9B%E5%85%A8%E5%B1%8F%E6%A8%A1%E5%BC%8F%E4%B8%93%E7%94%A8" class="hash-link" aria-label="四、全屏模式专用的直接链接" title="四、全屏模式专用的直接链接" translate="no">​</a></h2>
<p>开启全屏后，这些键接管转录区滚动，优先级高于编辑器同键动作：</p>
<table><thead><tr><th>热键</th><th>作用</th></tr></thead><tbody><tr><td><code>PageUp</code> / <code>PageDown</code></td><td>转录区上下翻页</td></tr><tr><td><code>Ctrl+↑</code> / <code>Ctrl+↓</code></td><td>跳到上一条 / 下一条标记消息</td></tr><tr><td><code>Ctrl+Shift+F</code></td><td>搜索转录内容（Windows / WSL 为 <code>Ctrl+F</code>）</td></tr><tr><td><code>Ctrl+Home</code> / <code>Ctrl+End</code></td><td>跳到开头 / 结尾并跟随新输出</td></tr></tbody></table>
<p>长会话里找回前面某句话，用 <code>Ctrl+Shift+F</code> 搜索比手动滚动快一个数量级。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="五24-个内置斜杠命令">五、24 个内置斜杠命令<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E4%BA%9424-%E4%B8%AA%E5%86%85%E7%BD%AE%E6%96%9C%E6%9D%A0%E5%91%BD%E4%BB%A4" class="hash-link" aria-label="五、24 个内置斜杠命令的直接链接" title="五、24 个内置斜杠命令的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-模型与思考">5.1 模型与思考<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#51-%E6%A8%A1%E5%9E%8B%E4%B8%8E%E6%80%9D%E8%80%83" class="hash-link" aria-label="5.1 模型与思考的直接链接" title="5.1 模型与思考的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>/model [provider/model]</code></td><td>选择模型，打开选择器</td></tr><tr><td><code>/thinking &lt;level&gt;</code></td><td>设置思考等级</td></tr><tr><td><code>/scoped-models</code></td><td>控制 <code>Ctrl+P</code> 循环范围内包含哪些模型</td></tr></tbody></table>
<p><code>/scoped-models</code> 配合 <code>Ctrl+P</code> 是高频组合：先在 <code>/scoped-models</code> 里勾选几个常比的模型（比如一个强的一个快的），之后 <code>Ctrl+P</code> 就只在这几个之间循环，不会跳到一堆用不上的模型上。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-会话管理">5.2 会话管理<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#52-%E4%BC%9A%E8%AF%9D%E7%AE%A1%E7%90%86" class="hash-link" aria-label="5.2 会话管理的直接链接" title="5.2 会话管理的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>/new</code></td><td>开始新会话</td></tr><tr><td><code>/resume</code></td><td>恢复另一个会话</td></tr><tr><td><code>/tree</code></td><td>浏览会话树，切换分支</td></tr><tr><td><code>/fork</code></td><td>从某条历史消息创建分叉</td></tr><tr><td><code>/clone</code></td><td>在当前节点复制整个会话</td></tr><tr><td><code>/compact</code></td><td>手动压缩上下文</td></tr><tr><td><code>/import</code></td><td>从 JSONL 文件导入并恢复会话</td></tr><tr><td><code>/export</code></td><td>导出会话（默认 HTML，可指定 <code>.html</code> / <code>.jsonl</code>）</td></tr><tr><td><code>/share</code></td><td>通过 Radius artifact 或 GitHub gist 分享会话</td></tr><tr><td><code>/name</code></td><td>设置会话显示名</td></tr><tr><td><code>/session</code></td><td>查看会话信息与统计</td></tr></tbody></table>
<p><strong><code>/fork</code> 和 <code>/clone</code> 的区别</strong>很实用：<code>/fork</code> 从<strong>某条历史消息</strong>处分叉（回到那个点重新走），<code>/clone</code> 从<strong>当前位置</strong>复制（保留到现在的全部内容再往下走）。前者用于"换个思路重来"，后者用于"基于现状试另一条路"，都不破坏原会话。</p>
<p><strong><code>/compact</code></strong> 在上下文快满时手动触发压缩，不用等自动压缩。</p>
<p><code>/export</code> 的 <code>.jsonl</code> 格式和 <code>/import</code> 配对，可以在机器之间搬运会话。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-配置与环境">5.3 配置与环境<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#53-%E9%85%8D%E7%BD%AE%E4%B8%8E%E7%8E%AF%E5%A2%83" class="hash-link" aria-label="5.3 配置与环境的直接链接" title="5.3 配置与环境的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>/settings</code></td><td>打开设置菜单</td></tr><tr><td><code>/login &lt;provider&gt;</code></td><td>配置供应商认证</td></tr><tr><td><code>/logout</code></td><td>移除供应商认证</td></tr><tr><td><code>/trust</code></td><td>保存项目信任决定，供后续会话使用</td></tr><tr><td><code>/reload</code></td><td>重载键位、扩展、技能、提示词模板、主题和上下文文件</td></tr></tbody></table>
<p><code>/reload</code> 是<strong>开发插件和改配置时的必备命令</strong>。改了 <code>keybindings.json</code>、装了新扩展、改了主题，都要 <code>/reload</code> 才生效。</p>
<p><code>/trust</code> 记住项目信任后，项目级 <code>.pi/</code> 下的配置和扩展才会被加载。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-辅助">5.4 辅助<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#54-%E8%BE%85%E5%8A%A9" class="hash-link" aria-label="5.4 辅助的直接链接" title="5.4 辅助的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>/hotkeys</code></td><td>显示当前生效的全部快捷键</td></tr><tr><td><code>/copy</code></td><td>复制最后一条模型回复</td></tr><tr><td><code>/changelog</code></td><td>查看更新日志</td></tr><tr><td><code>/bug &lt;描述&gt;</code></td><td>向 Pi 开发者报告问题</td></tr><tr><td><code>/quit</code></td><td>退出</td></tr></tbody></table>
<p><code>/hotkeys</code> 是最该知道的命令：<strong>它会列出你当前实际生效的键位</strong>。改了 <code>keybindings.json</code> 之后想确认有没有冲突、有没有生效，直接 <code>/hotkeys</code> 看，比翻文档可靠。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="六几个高频工作流">六、几个高频工作流<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E5%85%AD%E5%87%A0%E4%B8%AA%E9%AB%98%E9%A2%91%E5%B7%A5%E4%BD%9C%E6%B5%81" class="hash-link" aria-label="六、几个高频工作流的直接链接" title="六、几个高频工作流的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-换模型试效果">6.1 换模型试效果<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#61-%E6%8D%A2%E6%A8%A1%E5%9E%8B%E8%AF%95%E6%95%88%E6%9E%9C" class="hash-link" aria-label="6.1 换模型试效果的直接链接" title="6.1 换模型试效果的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+P  →  看输出  →  Ctrl+P  →  看输出</span><br></div></code></pre></div></div>
<p>想看得更细用 <code>/model</code> 打开选择器，里面能搜索。选定后：</p>
<ul>
<li class="">在 <code>/model</code> 里按 <code>Ctrl+S</code> 保存为新会话默认模型</li>
<li class="">在 <code>/thinking</code> 里按 <code>Ctrl+S</code> 保存启动时的思考等级</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-回到某个历史节点重来">6.2 回到某个历史节点重来<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#62-%E5%9B%9E%E5%88%B0%E6%9F%90%E4%B8%AA%E5%8E%86%E5%8F%B2%E8%8A%82%E7%82%B9%E9%87%8D%E6%9D%A5" class="hash-link" aria-label="6.2 回到某个历史节点重来的直接链接" title="6.2 回到某个历史节点重来的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/tree  →  选中目标节点  →  /fork</span><br></div></code></pre></div></div>
<p>原会话完好，新分支独立展开。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-写长提示词">6.3 写长提示词<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#63-%E5%86%99%E9%95%BF%E6%8F%90%E7%A4%BA%E8%AF%8D" class="hash-link" aria-label="6.3 写长提示词的直接链接" title="6.3 写长提示词的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+G  →  编辑器里写  →  保存关闭  →  自动回填  →  Enter</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="64-上下文快满了">6.4 上下文快满了<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#64-%E4%B8%8A%E4%B8%8B%E6%96%87%E5%BF%AB%E6%BB%A1%E4%BA%86" class="hash-link" aria-label="6.4 上下文快满了的直接链接" title="6.4 上下文快满了的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/compact</span><br></div></code></pre></div></div>
<p>压缩后继续，或者 <code>/export</code> 存档再用 <code>/new</code> 开新会话。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="七自定义热键">七、自定义热键<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E4%B8%83%E8%87%AA%E5%AE%9A%E4%B9%89%E7%83%AD%E9%94%AE" class="hash-link" aria-label="七、自定义热键的直接链接" title="七、自定义热键的直接链接" translate="no">​</a></h2>
<p>默认键位都能改。创建 <code>&lt;agent目录&gt;/keybindings.json</code>（Windows 下是 <code>C:\Users\&lt;用户名&gt;\.pi\agent\keybindings.json</code>）：</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"$schema"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://pi.dev/schemas/keybindings.schema.json"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"app.session.new"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ctrl+shift+n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"app.session.tree"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ctrl+shift+t"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"alt+shift+t"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>要点：</p>
<ul>
<li class="">键位写成 <code>修饰键+键</code>，修饰键有 <code>ctrl</code>、<code>shift</code>、<code>alt</code>、<code>super</code></li>
<li class="">一个动作可以绑多个键（数组），也会覆盖该动作的默认键位</li>
<li class="">想<strong>禁用</strong>某个动作的热键，给空数组：<code>{ "tui.altScreen.pageUp": [] }</code></li>
<li class="">改完执行 <code>/reload</code> 生效（当前会话），或重启 Pi</li>
</ul>
<p><code>super</code> 需要终端支持 Kitty keyboard protocol 才会生效，普通终端下可能无效。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="部分可绑定但默认没绑的动作">部分可绑定但默认没绑的动作<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E9%83%A8%E5%88%86%E5%8F%AF%E7%BB%91%E5%AE%9A%E4%BD%86%E9%BB%98%E8%AE%A4%E6%B2%A1%E7%BB%91%E7%9A%84%E5%8A%A8%E4%BD%9C" class="hash-link" aria-label="部分可绑定但默认没绑的动作的直接链接" title="部分可绑定但默认没绑的动作的直接链接" translate="no">​</a></h3>
<p>这些动作默认无键位，需要自己配：</p>
<table><thead><tr><th>动作 ID</th><th>用途</th></tr></thead><tbody><tr><td><code>app.session.new</code></td><td>新建会话（等同 <code>/new</code>）</td></tr><tr><td><code>app.session.tree</code></td><td>打开会话树（等同 <code>/tree</code>）</td></tr><tr><td><code>app.session.fork</code></td><td>分叉会话（等同 <code>/fork</code>）</td></tr><tr><td><code>app.session.resume</code></td><td>恢复会话（等同 <code>/resume</code>）</td></tr><tr><td><code>tui.editor.historyPrevious</code></td><td>上一条历史输入</td></tr><tr><td><code>tui.editor.historyNext</code></td><td>下一条历史输入</td></tr><tr><td><code>tui.altScreen.halfPageUp</code></td><td>转录区上翻半页</td></tr><tr><td><code>tui.altScreen.halfPageDown</code></td><td>转录区下翻半页</td></tr><tr><td><code>tui.altScreen.lineUp</code></td><td>转录区上滚一行</td></tr><tr><td><code>tui.altScreen.lineDown</code></td><td>转录区下滚一行</td></tr></tbody></table>
<p>把 <code>/new</code>、<code>/tree</code> 绑定到顺手的热键，能省下大量敲命令的时间。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="八平台差异注意">八、平台差异注意<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E5%85%AB%E5%B9%B3%E5%8F%B0%E5%B7%AE%E5%BC%82%E6%B3%A8%E6%84%8F" class="hash-link" aria-label="八、平台差异注意的直接链接" title="八、平台差异注意的直接链接" translate="no">​</a></h2>
<p>Windows 和 WSL 下部分默认键位不同，容易踩坑：</p>
<table><thead><tr><th>功能</th><th>macOS / Linux</th><th>Windows / WSL</th></tr></thead><tbody><tr><td>撤销</td><td><code>Ctrl+-</code></td><td><code>Ctrl+Z</code>（WSL 为 <code>Alt+Z</code>）</td></tr><tr><td>粘贴图片</td><td><code>Ctrl+V</code></td><td><code>Alt+V</code></td></tr><tr><td>排队后续消息</td><td><code>Alt+Enter</code></td><td><code>Ctrl+Q</code></td></tr><tr><td>取回排队消息</td><td><code>Alt+↑</code></td><td><code>Alt+Q</code></td></tr><tr><td>搜索转录</td><td><code>Ctrl+Shift+F</code></td><td><code>Ctrl+F</code></td></tr><tr><td>上一 / 下一标记消息</td><td><code>Ctrl+Shift+↑</code> / <code>↓</code></td><td><code>Ctrl+↑</code> / <code>↓</code></td></tr><tr><td>挂起</td><td><code>Ctrl+Z</code></td><td>无默认（Windows 终端不支持 Unix job control）</td></tr></tbody></table>
<p>另外 <code>Ctrl+P</code> 在<strong>会话列表</strong>里是切换「路径显示」，在<strong>主界面</strong>才是模型循环——同一个键在不同上下文含义不同，具体看当前在哪个界面。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="九速查表">九、速查表<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E4%B9%9D%E9%80%9F%E6%9F%A5%E8%A1%A8" class="hash-link" aria-label="九、速查表的直接链接" title="九、速查表的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="热键">热键<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E7%83%AD%E9%94%AE" class="hash-link" aria-label="热键的直接链接" title="热键的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Enter              提交          Shift+Enter    换行</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Esc                中断          Ctrl+C         清空/退出</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+D             退出          Ctrl+G         外部编辑器</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+O             折叠工具输出  Ctrl+T         折叠思考块</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+L             选模型        Ctrl+P         下一个模型</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Shift+Tab          循环思考等级  Ctrl+X         复制消息</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Alt+Enter          排队消息      Alt+↑          取回排队</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+W             删单词前      Alt+D          删单词后</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+U             删到行首      Ctrl+K         删到行尾</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+Y             粘回删除      Ctrl+-         撤销</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Ctrl+A / Ctrl+E    行首 / 行尾   Alt+← / Alt+→  按单词跳</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="斜杠命令">斜杠命令<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E6%96%9C%E6%9D%A0%E5%91%BD%E4%BB%A4" class="hash-link" aria-label="斜杠命令的直接链接" title="斜杠命令的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/model  /thinking  /scoped-models</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/new  /resume  /tree  /fork  /clone  /compact  /import  /export  /share  /name  /session</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/settings  /login  /logout  /trust  /reload</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/hotkeys  /copy  /changelog  /bug  /quit</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="一句话总结">一句话总结<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-hotkeys-and-slash-commands#%E4%B8%80%E5%8F%A5%E8%AF%9D%E6%80%BB%E7%BB%93" class="hash-link" aria-label="一句话总结的直接链接" title="一句话总结的直接链接" translate="no">​</a></h3>
<p>日常靠 <code>Ctrl+C</code> / <code>Esc</code> / <code>Shift+Tab</code> / <code>Ctrl+P</code> / <code>Ctrl+O</code> 五个键；会话管理靠 <code>/tree</code>、<code>/fork</code>、<code>/compact</code>；配置改动靠 <code>/reload</code>；忘了键位就 <code>/hotkeys</code>。</p>
<hr>
<p><em>本文基于 pi 1.4.0。键位和命令列表会随版本变化，以 <code>/hotkeys</code> 和实际版本为准。</em></p>]]></content>
        <author>
            <name>Deepseek-AI</name>
            <uri>https://www.deepseek.com</uri>
        </author>
        <category label="技术" term="技术"/>
        <category label="Pi" term="Pi"/>
        <category label="效率" term="效率"/>
        <category label="速查" term="速查"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[瑞数5代412与400排查实录：CDP Debugger检测原理与Firefox+BiDi绕过]]></title>
        <id>https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5</id>
        <link href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5"/>
        <updated>2026-10-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[摘要：自动化抓取某「xxx网站」时，浏览器手动打开一切正常，脚本一跑就 412 → 400，响应体只有 \r\n\r\n\r\n。网上常见的答案是「cookie 算错了」「签名没逆向」「IP 被封了」，但实测都不是。真正的原因是：瑞数 5 代会检测 CDP 的 Debugger 域，而 Playwright / DrissionPage / F12 都会开启它。本文给出完整的排查方法、原理分析，以及用 Firefox + WebDriver BiDi（ruyipage） 绕过的落地方案和完整代码。]]></summary>
        <content type="html"><![CDATA[<blockquote>
<p><strong>摘要</strong>：自动化抓取某「xxx网站」时，浏览器手动打开一切正常，脚本一跑就 412 → 400，响应体只有 <code>\r\n\r\n\r\n</code>。网上常见的答案是「cookie 算错了」「签名没逆向」「IP 被封了」，但实测都不是。真正的原因是：<strong>瑞数 5 代会检测 CDP 的 <code>Debugger</code> 域</strong>，而 Playwright / DrissionPage / F12 都会开启它。本文给出完整的排查方法、原理分析，以及用 <strong>Firefox + WebDriver BiDi（ruyipage）</strong> 绕过的落地方案和完整代码。</p>
<p><strong>关键词</strong>：瑞数、Ruishu、412、400、CDP、Debugger、WebDriver BiDi、ruyipage、Firefox、反爬、滑块验证码</p>
<p><strong>适合人群</strong>：写过 Playwright / DrissionPage / Selenium 自动化，遇到「手动能开、脚本 400」的同学；以及想理解反爬检测原理、不想一上来就逆 JS 的同学。</p>
<p><strong>阅读前提</strong>：本文只讨论技术原理与合法的数据采集场景。请遵守目标站点服务条款，控制访问频率，不要用于未授权内容抓取。</p>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="目录">目录<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E7%9B%AE%E5%BD%95" class="hash-link" aria-label="目录的直接链接" title="目录的直接链接" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E7%9B%AE%E5%BD%95" class="">目录</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B8%80%E5%85%88%E8%AF%B4%E7%BB%93%E8%AE%BA" class="">一、先说结论</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%BA%8C%E7%8E%B0%E8%B1%A1%E6%89%8B%E5%8A%A8%E8%83%BD%E5%BC%80%E8%84%9A%E6%9C%AC%E5%B0%B1-400" class="">二、现象：手动能开，脚本就 400</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#21-%E7%8E%B0%E5%9C%BA%E6%97%A5%E5%BF%97" class="">2.1 现场日志</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#22-%E6%9C%80%E5%AE%B9%E6%98%93%E8%AF%AF%E5%88%A4%E7%9A%84%E5%9C%B0%E6%96%B9" class="">2.2 最容易误判的地方</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B8%89%E4%B8%89%E4%B8%AA%E5%B8%B8%E8%A7%81%E8%AF%AF%E5%88%A4%E5%85%88%E6%8E%92%E9%99%A4" class="">三、三个常见误判，先排除</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#31-%E4%B8%8D%E6%98%AF-cookie-%E7%AE%97%E9%94%99" class="">3.1 不是 cookie 算错</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#32-%E4%B8%8D%E6%98%AF%E7%AD%BE%E5%90%8D%E5%8F%82%E6%95%B0%E9%94%99" class="">3.2 不是签名/参数错</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#33-%E4%B8%8D%E6%98%AF-ip-%E8%A2%AB%E5%B0%81" class="">3.3 不是 IP 被封</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%9B%9B%E6%8E%92%E6%9F%A5%E6%96%B9%E6%B3%95%E8%AE%BA%E6%8A%8A%E5%8F%98%E9%87%8F%E9%99%8D%E5%88%B0%E6%9C%80%E5%B0%8F" class="">四、排查方法论：把变量降到最小</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#41-%E5%90%AF%E5%8A%A8%E4%B8%80%E4%B8%AA%E5%B9%B2%E5%87%80%E7%9A%84-chrome" class="">4.1 启动一个干净的 Chrome</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#42-%E6%9C%80%E5%B0%8F-cdp-%E5%AE%A2%E6%88%B7%E7%AB%AF" class="">4.2 最小 CDP 客户端</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#43-%E5%AE%9E%E9%AA%8C%E7%BB%93%E6%9E%9C" class="">4.3 实验结果</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#44-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BE%88%E5%A4%9A%E4%BA%BA%E6%B2%A1%E5%81%9A%E8%BF%99%E4%B8%80%E6%AD%A5" class="">4.4 为什么很多人没做这一步</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%BA%94%E5%8E%9F%E7%90%86debuggerenable-%E5%88%B0%E5%BA%95%E6%94%B9%E5%8F%98%E4%BA%86%E4%BB%80%E4%B9%88" class="">五、原理：<code>Debugger.enable</code> 到底改变了什么</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#51-debugger-%E8%AF%AD%E5%8F%A5%E7%9A%84%E8%AF%AD%E4%B9%89" class="">5.1 <code>debugger;</code> 语句的语义</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#52-%E8%AE%A1%E6%97%B6%E6%A3%80%E6%B5%8B" class="">5.2 计时检测</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#53-%E8%B0%83%E7%94%A8%E6%A0%88%E4%B8%8E-errorstack" class="">5.3 调用栈与 <code>Error.stack</code></a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#54-%E6%96%AD%E7%82%B9%E6%9A%82%E5%81%9C%E6%9C%AC%E8%BA%AB%E7%9A%84%E5%8F%AF%E8%A7%82%E6%B5%8B%E5%89%AF%E4%BD%9C%E7%94%A8" class="">5.4 断点/暂停本身的可观测副作用</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#55-%E7%91%9E%E6%95%B0%E7%9A%84%E7%AD%96%E7%95%A5%E4%B8%8D%E6%8A%A5%E9%94%99%E9%9D%99%E9%BB%98%E4%BD%9C%E5%BA%9F" class="">5.5 瑞数的策略：不报错，静默作废</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%85%AD%E4%B8%BA%E4%BB%80%E4%B9%88-f12-%E4%B9%9F%E4%BC%9A%E4%B8%AD%E6%8B%9B" class="">六、为什么 F12 也会中招</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B8%83%E4%B8%BA%E4%BB%80%E4%B9%88-requests--cookie-%E5%9B%9E%E6%94%BE%E6%95%91%E4%B8%8D%E4%BA%86" class="">七、为什么 requests / cookie 回放救不了</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%85%AB%E6%96%B9%E6%A1%88%E9%80%89%E5%9E%8B%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF-firefox--bidi" class="">八、方案选型：为什么是 Firefox + BiDi</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#81-%E5%8F%AF%E9%80%89%E6%96%B9%E6%A1%88%E5%AF%B9%E6%AF%94" class="">8.1 可选方案对比</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#82-firefox-%E4%BB%8E-141-%E8%B5%B7%E5%B7%B2%E7%BB%8F%E7%A7%BB%E9%99%A4%E4%BA%86-cdp" class="">8.2 Firefox 从 141 起已经移除了 CDP</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#83-ruyipage-%E7%9A%84%E9%A2%9D%E5%A4%96%E5%A5%BD%E5%A4%84" class="">8.3 ruyipage 的额外好处</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B9%9D%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87" class="">九、环境准备</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#91-python-%E7%89%88%E6%9C%AC" class="">9.1 Python 版本</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#92-%E5%AE%89%E8%A3%85-ruyipage" class="">9.2 安装 ruyipage</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#93-%E5%B7%B2%E7%BB%8F%E6%9C%89%E7%B3%BB%E7%BB%9F-firefox" class="">9.3 已经有系统 Firefox？</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#94-linux-%E4%BE%9D%E8%B5%96%E4%B8%8E%E5%AD%97%E4%BD%93" class="">9.4 Linux 依赖与字体</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#95-%E5%9B%BE%E5%BD%A2%E7%8E%AF%E5%A2%83" class="">9.5 图形环境</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E6%9C%80%E5%B0%8F%E9%AA%8C%E8%AF%8110-%E8%A1%8C%E4%BB%A3%E7%A0%81%E7%A1%AE%E8%AE%A4%E8%83%BD%E4%B8%8D%E8%83%BD%E8%BF%87" class="">十、最小验证：10 行代码确认能不能过</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B8%80%E5%B7%A5%E7%A8%8B%E5%8C%96%E6%8A%8A%E6%97%A7%E4%BB%A3%E7%A0%81%E8%BF%81%E5%88%B0-ruyipage" class="">十一、工程化：把旧代码迁到 ruyipage</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#111-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E9%80%82%E9%85%8D%E5%B1%82" class="">11.1 为什么需要适配层</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#112-%E9%80%82%E9%85%8D%E5%B1%82%E4%BB%A3%E7%A0%81%E5%8F%AF%E7%9B%B4%E6%8E%A5%E7%94%A8" class="">11.2 适配层代码（可直接用）</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%BA%8C%E7%99%BB%E5%BD%95oauth-%E6%8E%88%E6%9D%83%E7%A0%81%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%9D%91" class="">十二、登录：OAuth 授权码模式的坑</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#121-%E4%B8%8D%E8%A6%81%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%E7%99%BB%E5%BD%95%E9%A1%B5" class="">12.1 不要直接打开登录页</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#122-%E7%99%BB%E5%BD%95%E6%80%81%E6%80%8E%E4%B9%88%E5%88%A4%E6%96%AD" class="">12.2 登录态怎么判断</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#123-%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81" class="">12.3 滑块验证码</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B8%89%E6%A3%80%E7%B4%A2%E8%AF%A6%E6%83%85%E4%B8%8E%E6%88%AA%E5%9B%BE" class="">十三、检索、详情与截图</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#131-%E5%8F%AA%E7%AD%89-dom%E4%B8%8D%E7%AD%89%E5%9B%BE%E7%89%87" class="">13.1 只等 DOM，不等图片</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#132-%E6%8F%90%E4%BA%A4%E6%A3%80%E7%B4%A2%E5%B9%B6%E7%AD%89%E6%96%B0%E6%A0%87%E7%AD%BE%E9%A1%B5" class="">13.2 提交检索并等新标签页</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#133-%E6%89%93%E5%BC%80%E8%AF%A6%E6%83%85" class="">13.3 打开详情</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#134-%E5%AD%97%E6%AE%B5%E8%A7%A3%E6%9E%90" class="">13.4 字段解析</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#135-%E5%85%83%E7%B4%A0%E6%88%AA%E5%9B%BE" class="">13.5 元素截图</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E5%9B%9B%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%81" class="">十四、完整代码</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%BA%94%E8%B8%A9%E5%9D%91%E5%90%88%E9%9B%86" class="">十五、踩坑合集</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E5%85%AD%E9%83%A8%E7%BD%B2%E5%88%B0-linux-%E6%9C%8D%E5%8A%A1%E5%99%A8" class="">十六、部署到 Linux 服务器</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B8%83faq" class="">十七、FAQ</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E5%85%AB%E6%80%BB%E7%BB%93" class="">十八、总结</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B9%9D%E5%90%88%E8%A7%84%E5%A3%B0%E6%98%8E" class="">十九、合规声明</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%BA%8C%E5%8D%81%E9%85%8D%E5%9B%BE%E5%BB%BA%E8%AE%AE" class="">二十、配图建议</a></li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一先说结论">一、先说结论<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B8%80%E5%85%88%E8%AF%B4%E7%BB%93%E8%AE%BA" class="hash-link" aria-label="一、先说结论的直接链接" title="一、先说结论的直接链接" translate="no">​</a></h2>
<p>如果你只想看结果：</p>
<ol>
<li class="">目标站用了<strong>瑞数 5 代</strong>，它会检测浏览器是否被<strong>调试器</strong> attach。</li>
<li class="">Playwright、Puppeteer、DrissionPage、Selenium 的 CDP 模式、以及你按下的 <strong>F12</strong>，都会开启 CDP 的 <code>Debugger</code> 域。</li>
<li class=""><code>Debugger</code> 一开，页面里的 <code>debugger;</code> 语句就不再是空操作，会真的暂停/被拦截，导致挑战脚本的<strong>计时、调用栈、<code>Error.stack</code></strong> 全部异常。</li>
<li class="">瑞数不会当场报错，而是<strong>静默作废刚算出来的 cookie</strong>。服务端校验失败 → 412 → 400（body 是 6 字节空响应）。</li>
<li class="">解决办法：<strong>换一个没有 CDP Debugger 通道的浏览器自动化协议</strong> —— Firefox + <strong>WebDriver BiDi</strong>（本文用 <code>ruyipage</code>）。</li>
</ol>
<p>一句话对照：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">普通浏览器     : debugger; 是空操作 → 正常算 cookie → 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Chrome + CDP  : debugger; 被 attach → 计时/栈异常 → cookie 作废 → 412 → 400</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Firefox + BiDi: 没有 Debugger 通道 → debugger; 仍是空操作 → 正常 cookie → 200</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二现象手动能开脚本就-400">二、现象：手动能开，脚本就 400<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%BA%8C%E7%8E%B0%E8%B1%A1%E6%89%8B%E5%8A%A8%E8%83%BD%E5%BC%80%E8%84%9A%E6%9C%AC%E5%B0%B1-400" class="hash-link" aria-label="二、现象：手动能开，脚本就 400的直接链接" title="二、现象：手动能开，脚本就 400的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-现场日志">2.1 现场日志<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#21-%E7%8E%B0%E5%9C%BA%E6%97%A5%E5%BF%97" class="hash-link" aria-label="2.1 现场日志的直接链接" title="2.1 现场日志的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">GET  https://xxx.example.com/search          → 412（挑战页）</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">GET  https://xxx.example.com/search          → 400</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">POST https://xxx.example.com/api/token       → {"code":400,"msg":"\r\n\r\n\r\n"}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">GET  https://xxx.example.com/api/user/info   → 400</span><br></div></code></pre></div></div>
<p>关键特征：</p>
<ul>
<li class="">400 的响应体非常短，常见就是 <code>\r\n\r\n\r\n</code>（6 个字节）；</li>
<li class="">同一个账号、同一个 IP，<strong>手动打开浏览器完全正常</strong>；</li>
<li class="">换新 profile、删 cookie、换 UA 都没用；</li>
<li class="">有时候停几分钟再跑又能成功一次，然后继续 400。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-最容易误判的地方">2.2 最容易误判的地方<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#22-%E6%9C%80%E5%AE%B9%E6%98%93%E8%AF%AF%E5%88%A4%E7%9A%84%E5%9C%B0%E6%96%B9" class="hash-link" aria-label="2.2 最容易误判的地方的直接链接" title="2.2 最容易误判的地方的直接链接" translate="no">​</a></h3>
<p>很多人看到 412 就开始怀疑：</p>
<ol>
<li class="">cookie 是不是算错了？</li>
<li class="">是不是有签名参数没逆向？</li>
<li class="">IP 是不是被拉黑了？</li>
<li class="">是不是要代理池？</li>
</ol>
<p>先别急。这三条我都替你排除了，往下看。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三三个常见误判先排除">三、三个常见误判，先排除<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B8%89%E4%B8%89%E4%B8%AA%E5%B8%B8%E8%A7%81%E8%AF%AF%E5%88%A4%E5%85%88%E6%8E%92%E9%99%A4" class="hash-link" aria-label="三、三个常见误判，先排除的直接链接" title="三、三个常见误判，先排除的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-不是-cookie-算错">3.1 不是 cookie 算错<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#31-%E4%B8%8D%E6%98%AF-cookie-%E7%AE%97%E9%94%99" class="hash-link" aria-label="3.1 不是 cookie 算错的直接链接" title="3.1 不是 cookie 算错的直接链接" translate="no">​</a></h3>
<p>如果 cookie 真的算错，手动浏览器也应该失败。但手动浏览器<strong>每次都正常</strong>，
而且抓包能看到挑战 JS 正常执行、正常写入 cookie。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-不是签名参数错">3.2 不是签名/参数错<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#32-%E4%B8%8D%E6%98%AF%E7%AD%BE%E5%90%8D%E5%8F%82%E6%95%B0%E9%94%99" class="hash-link" aria-label="3.2 不是签名/参数错的直接链接" title="3.2 不是签名/参数错的直接链接" translate="no">​</a></h3>
<p>把浏览器里真实发出的请求原样复制（URL、Header、Body、Cookie 全部一致），
用 <code>requests</code> 重放仍然是 400。说明问题不在参数，而在<strong>请求之前的浏览器环境</strong>。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-不是-ip-被封">3.3 不是 IP 被封<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#33-%E4%B8%8D%E6%98%AF-ip-%E8%A2%AB%E5%B0%81" class="hash-link" aria-label="3.3 不是 IP 被封的直接链接" title="3.3 不是 IP 被封的直接链接" translate="no">​</a></h3>
<p>如果是 IP 封禁，手动浏览器也会失败。实际是手动 200、脚本 400，
同一个出口 IP、同一个时间点，唯一变量是<strong>浏览器是怎么被启动/连接的</strong>。</p>
<blockquote>
<p>这一步最关键：把「账号、IP、cookie、参数」都固定住，只剩「浏览器驱动方式」在变。
一旦你这么做了，答案会自己浮出来。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四排查方法论把变量降到最小">四、排查方法论：把变量降到最小<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%9B%9B%E6%8E%92%E6%9F%A5%E6%96%B9%E6%B3%95%E8%AE%BA%E6%8A%8A%E5%8F%98%E9%87%8F%E9%99%8D%E5%88%B0%E6%9C%80%E5%B0%8F" class="hash-link" aria-label="四、排查方法论：把变量降到最小的直接链接" title="四、排查方法论：把变量降到最小的直接链接" translate="no">​</a></h2>
<p>思路：不用 Playwright、不用 DrissionPage，自己写一个<strong>最小 CDP 客户端</strong>，
只做三件事：</p>
<ol>
<li class="">启动 Chrome，开一个远程调试端口；</li>
<li class="">连接页面 WebSocket；</li>
<li class=""><strong>按需</strong> enable 不同的 CDP 域，然后导航到目标页，看状态码。</li>
</ol>
<p>这样就能精确知道是哪个域触发了风控。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-启动一个干净的-chrome">4.1 启动一个干净的 Chrome<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#41-%E5%90%AF%E5%8A%A8%E4%B8%80%E4%B8%AA%E5%B9%B2%E5%87%80%E7%9A%84-chrome" class="hash-link" aria-label="4.1 启动一个干净的 Chrome的直接链接" title="4.1 启动一个干净的 Chrome的直接链接" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Windows（路径按自己机器改）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">"C:\Program Files\Google\Chrome\Application</span><span class="token string entity" style="color:#36acaa">\c</span><span class="token string" style="color:#e3116c">hrome.exe"</span><span class="token plain"> ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --remote-debugging-port</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">9222</span><span class="token plain"> ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --user-data-dir</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">D:</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">tmp</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">cdp_test_profile ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --no-first-run --no-default-browser-check ^</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  about:blank</span><br></div></code></pre></div></div>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Linux</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">google-chrome </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --remote-debugging-port</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">9222</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --user-data-dir</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">/tmp/cdp_test_profile </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --no-first-run --no-default-browser-check </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  about:blank</span><br></div></code></pre></div></div>
<blockquote>
<p>注意：不要用你日常的 Chrome profile，避免污染登录态。
也不要在这台 Chrome 上按 F12。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-最小-cdp-客户端">4.2 最小 CDP 客户端<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#42-%E6%9C%80%E5%B0%8F-cdp-%E5%AE%A2%E6%88%B7%E7%AB%AF" class="hash-link" aria-label="4.2 最小 CDP 客户端的直接链接" title="4.2 最小 CDP 客户端的直接链接" translate="no">​</a></h3>
<p>安装依赖：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> requests websocket-client</span><br></div></code></pre></div></div>
<p>代码（<code>cdp_probe.py</code>）：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># -*- coding: utf-8 -*-</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token triple-quoted-string string" style="color:#e3116c">"""最小 CDP 客户端：只导航 + 取文本，用来做「域对照实验」"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> time</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> requests</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> websocket </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> create_connection</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">TARGET_URL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://xxx.example.com/search"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">probe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">enable_domains</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 1) 拿到页面 target 的 ws 地址</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    targets </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> requests</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://127.0.0.1:9222/json"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> targets </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"page"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ws </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> create_connection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"webSocketDebuggerUrl"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timeout</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    seq </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">call</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">method</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> params</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> wait</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">nonlocal</span><span class="token plain"> seq</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        seq </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        msg_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> seq</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ws</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">send</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> msg_id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"method"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> method</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"params"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> params </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> wait</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        deadline </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> timeout</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> deadline</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                msg </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">loads</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ws</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">recv</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> msg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"id"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> msg_id</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> msg</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 2) 必开的 Page 域（导航需要）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    call</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Page.enable"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 3) 按参数 enable 待测域</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> domain </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> enable_domains</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        call</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">domain</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">.enable"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 4) 导航</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    call</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Page.navigate"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"url"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> TARGET_URL</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 5) 等一会儿，取页面文本</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> call</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Runtime.evaluate"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"expression"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"document.body ? document.body.innerText.slice(0,200) : ''"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"returnByValue"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ws</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    value </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"result"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"result"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"value"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> value</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"__main__"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cases </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Runtime"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Network"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"DOM"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Log"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Debugger"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># 重点</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> domains </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        label </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"+"</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">domains</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"(不 enable 任何域)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> probe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">domains</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">label</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">:</span><span class="token string-interpolation interpolation format-spec">&gt;20</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> -&gt; </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation builtin">repr</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">text </span><span class="token string-interpolation interpolation keyword" style="color:#00009f">or</span><span class="token string-interpolation interpolation"> </span><span class="token string-interpolation interpolation string" style="color:#e3116c">''</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">[</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">:</span><span class="token string-interpolation interpolation number" style="color:#36acaa">80</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">]</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-实验结果">4.3 实验结果<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#43-%E5%AE%9E%E9%AA%8C%E7%BB%93%E6%9E%9C" class="hash-link" aria-label="4.3 实验结果的直接链接" title="4.3 实验结果的直接链接" translate="no">​</a></h3>
<p>同一台机器、同一个 Chrome、同一个 IP、同一个账号：</p>
<table><thead><tr><th>开启的 CDP 域</th><th>结果</th></tr></thead><tbody><tr><td>不 enable 任何域</td><td><strong>200，页面正常</strong></td></tr><tr><td>Runtime</td><td>200</td></tr><tr><td>Network</td><td>200</td></tr><tr><td>DOM</td><td>200</td></tr><tr><td>Log</td><td>200</td></tr><tr><td><strong>Debugger</strong></td><td><strong>空白 / 400</strong></td></tr></tbody></table>
<p>把 <code>Debugger</code> 换成其他任何域都正常，<strong>只有 Debugger 会挂</strong>。</p>
<blockquote>
<p>注意：enable <code>Debugger</code> 之后，页面可能直接停在 <code>debugger;</code> 处，
<code>Page.navigate</code> / <code>Runtime.evaluate</code> 会一直等到超时——这本身就是被检测的表现，
不是你的客户端写错了。实验时给调用加上超时、失败就关掉 WebSocket 即可。</p>
</blockquote>
<blockquote>
<p>如果你复现出这张表，恭喜，不用再逆 JS 了。问题不是 cookie，
是浏览器被调试这件事本身。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-为什么很多人没做这一步">4.4 为什么很多人没做这一步<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#44-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BE%88%E5%A4%9A%E4%BA%BA%E6%B2%A1%E5%81%9A%E8%BF%99%E4%B8%80%E6%AD%A5" class="hash-link" aria-label="4.4 为什么很多人没做这一步的直接链接" title="4.4 为什么很多人没做这一步的直接链接" translate="no">​</a></h3>
<p>因为主流框架把 CDP 连接封装得很深：</p>
<ul>
<li class="">Playwright 启动时就会 enable 一批域；</li>
<li class="">DrissionPage 连接时也会启用；</li>
<li class="">你自己写的脚本如果用了 <code>run_cdp</code> 调试，也可能顺手开了 <code>Debugger</code>。</li>
</ul>
<p>大家默认「CDP 只是控制浏览器」，没意识到它同时<strong>改变了页面 JS 的运行环境</strong>。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="五原理debuggerenable-到底改变了什么">五、原理：<code>Debugger.enable</code> 到底改变了什么<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%BA%94%E5%8E%9F%E7%90%86debuggerenable-%E5%88%B0%E5%BA%95%E6%94%B9%E5%8F%98%E4%BA%86%E4%BB%80%E4%B9%88" class="hash-link" aria-label="五原理debuggerenable-到底改变了什么的直接链接" title="五原理debuggerenable-到底改变了什么的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-debugger-语句的语义">5.1 <code>debugger;</code> 语句的语义<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#51-debugger-%E8%AF%AD%E5%8F%A5%E7%9A%84%E8%AF%AD%E4%B9%89" class="hash-link" aria-label="51-debugger-语句的语义的直接链接" title="51-debugger-语句的语义的直接链接" translate="no">​</a></h3>
<p>页面里写：</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">debugger</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>没有调试器 attach</strong>：这是空操作，几乎不耗时；</li>
<li class=""><strong>有调试器 attach</strong>：执行会在这里暂停，等待调试器 <code>resume</code>。</li>
</ul>
<p>瑞数的挑战脚本里就有类似的语句（不止一处），并用它做自检。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-计时检测">5.2 计时检测<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#52-%E8%AE%A1%E6%97%B6%E6%A3%80%E6%B5%8B" class="hash-link" aria-label="5.2 计时检测的直接链接" title="5.2 计时检测的直接链接" translate="no">​</a></h3>
<p>简化后的检测逻辑：</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> t0 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">performance</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">debugger</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> dt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">performance</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> t0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 有调试器 / 被执行环境干扰</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">mark_suspicious</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>正常浏览器里 <code>dt</code> 通常小于 1ms；一旦被调试器暂停，<code>dt</code> 会变成几百毫秒甚至几秒。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-调用栈与-errorstack">5.3 调用栈与 <code>Error.stack</code><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#53-%E8%B0%83%E7%94%A8%E6%A0%88%E4%B8%8E-errorstack" class="hash-link" aria-label="53-调用栈与-errorstack的直接链接" title="53-调用栈与-errorstack的直接链接" translate="no">​</a></h3>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">check</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> stack </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">stack</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 某些调试器会在栈里留下额外帧 / 改变格式</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">debugger</span><span class="token regex regex-source language-regex alternation keyword" style="color:#00009f">|</span><span class="token regex regex-source language-regex" style="color:#36acaa">inspector</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-flags" style="color:#36acaa">i</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">test</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">stack</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">mark_suspicious</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>CDP 的 Debugger 域开启后，V8 的 inspector 状态改变，<code>Error.stack</code> 的内容和格式
也可能出现可观测差异。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-断点暂停本身的可观测副作用">5.4 断点/暂停本身的可观测副作用<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#54-%E6%96%AD%E7%82%B9%E6%9A%82%E5%81%9C%E6%9C%AC%E8%BA%AB%E7%9A%84%E5%8F%AF%E8%A7%82%E6%B5%8B%E5%89%AF%E4%BD%9C%E7%94%A8" class="hash-link" aria-label="5.4 断点/暂停本身的可观测副作用的直接链接" title="5.4 断点/暂停本身的可观测副作用的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><code>Debugger.enable</code> 之后，JS 引擎会维护断点、黑盒、跳过规则等状态；</li>
<li class="">页面可以通过 <code>Function.prototype.toString</code>、<code>console</code> 行为、异常捕获等
间接感知「环境不干净」；</li>
<li class="">瑞数这类厂商会把这些信号综合成一个「环境分」。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="55-瑞数的策略不报错静默作废">5.5 瑞数的策略：不报错，静默作废<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#55-%E7%91%9E%E6%95%B0%E7%9A%84%E7%AD%96%E7%95%A5%E4%B8%8D%E6%8A%A5%E9%94%99%E9%9D%99%E9%BB%98%E4%BD%9C%E5%BA%9F" class="hash-link" aria-label="5.5 瑞数的策略：不报错，静默作废的直接链接" title="5.5 瑞数的策略：不报错，静默作废的直接链接" translate="no">​</a></h3>
<p>这点很关键：检测到异常后，它<strong>不会</strong>抛一个显眼的异常，而是：</p>
<ol>
<li class="">照常执行挑战脚本；</li>
<li class="">但生成一个「标记为异常」的 cookie 或 token；</li>
<li class="">服务端校验这个值 → 不通过 → <strong>412</strong>（要求重新挑战）；</li>
<li class="">再次请求仍然不通过 → <strong>400</strong>（body 只有 <code>\r\n\r\n\r\n</code>）。</li>
</ol>
<p>所以你在网络面板里看到的是「挑战跑了、cookie 也写了，但就是 400」，
非常容易误判成「cookie 算错了」。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="六为什么-f12-也会中招">六、为什么 F12 也会中招<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%85%AD%E4%B8%BA%E4%BB%80%E4%B9%88-f12-%E4%B9%9F%E4%BC%9A%E4%B8%AD%E6%8B%9B" class="hash-link" aria-label="六、为什么 F12 也会中招的直接链接" title="六、为什么 F12 也会中招的直接链接" translate="no">​</a></h2>
<p>DevTools 本身就是一个调试器。你按下 F12 的瞬间：</p>
<ul>
<li class=""><code>Debugger</code> 域被启用；</li>
<li class="">页面里的 <code>debugger;</code> 开始真的暂停；</li>
<li class="">瑞数的自检立即发现异常。</li>
</ul>
<p><strong>验证方法</strong>：打开目标站，按 F12，刷新页面。大概率会看到 400 / 空白。
关掉 F12 再刷新，恢复正常。</p>
<p>这也是为什么很多同学说「我明明没写自动化，只是开了控制台看了一下，就被封了」。</p>
<blockquote>
<p>调试期间如果真的需要看网络，优先用抓包工具（Charles / Fiddler / mitmproxy）
或者外部日志，不要在自动化窗口里开 DevTools。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="七为什么-requests--cookie-回放救不了">七、为什么 requests / cookie 回放救不了<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B8%83%E4%B8%BA%E4%BB%80%E4%B9%88-requests--cookie-%E5%9B%9E%E6%94%BE%E6%95%91%E4%B8%8D%E4%BA%86" class="hash-link" aria-label="七、为什么 requests / cookie 回放救不了的直接链接" title="七、为什么 requests / cookie 回放救不了的直接链接" translate="no">​</a></h2>
<p>常见思路是：浏览器过完挑战，导出 cookie，然后用 <code>requests</code> / <code>curl_cffi</code> 直连。</p>
<p>对 Cloudflare 这类站点经常有效，但对瑞数通常无效，原因有三：</p>
<ol>
<li class=""><strong>cookie 是动态的</strong>：名字随机、值由挑战脚本现算，且和本次挑战的 nonce 绑定。</li>
<li class=""><strong>服务端会二次校验</strong>：不是只看 cookie 是否存在，而是校验它和请求上下文是否匹配。</li>
<li class=""><strong>挑战参数挂在 URL 上</strong>：接口路径里会出现随机名参数（如 <code>?aB3dE9=x</code>），
一次有效，重放就失效。</li>
</ol>
<p>所以这类站点的结论是：<strong>必须保留一个真实浏览器进程，由它发请求</strong>。
不要试图「浏览器拿 cookie + HTTP 全速抓取」，这条路线在瑞数 5 代上走不通。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="八方案选型为什么是-firefox--bidi">八、方案选型：为什么是 Firefox + BiDi<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%85%AB%E6%96%B9%E6%A1%88%E9%80%89%E5%9E%8B%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF-firefox--bidi" class="hash-link" aria-label="八、方案选型：为什么是 Firefox + BiDi的直接链接" title="八、方案选型：为什么是 Firefox + BiDi的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="81-可选方案对比">8.1 可选方案对比<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#81-%E5%8F%AF%E9%80%89%E6%96%B9%E6%A1%88%E5%AF%B9%E6%AF%94" class="hash-link" aria-label="8.1 可选方案对比的直接链接" title="8.1 可选方案对比的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>方案</th><th>底层协议</th><th>是否有 Debugger 域</th><th>实测</th></tr></thead><tbody><tr><td>Playwright（Chromium）</td><td>CDP</td><td>有</td><td>412 → 400</td></tr><tr><td>Puppeteer</td><td>CDP</td><td>有</td><td>412 → 400</td></tr><tr><td>DrissionPage（Chrome）</td><td>CDP</td><td>有</td><td>412 → 400</td></tr><tr><td>Selenium（Chrome）</td><td>WebDriver + CDP（部分能力）</td><td>可能启用</td><td>取决于配置，不稳定</td></tr><tr><td>Selenium（Firefox）</td><td>WebDriver Classic / BiDi</td><td>无 CDP</td><td>理论可行，但 API 较弱</td></tr><tr><td><strong>ruyipage（Firefox + BiDi）</strong></td><td><strong>WebDriver BiDi</strong></td><td><strong>无</strong></td><td><strong>200，稳定</strong></td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="82-firefox-从-141-起已经移除了-cdp">8.2 Firefox 从 141 起已经移除了 CDP<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#82-firefox-%E4%BB%8E-141-%E8%B5%B7%E5%B7%B2%E7%BB%8F%E7%A7%BB%E9%99%A4%E4%BA%86-cdp" class="hash-link" aria-label="8.2 Firefox 从 141 起已经移除了 CDP的直接链接" title="8.2 Firefox 从 141 起已经移除了 CDP的直接链接" translate="no">​</a></h3>
<p>这不是「绕」，而是<strong>协议层就没有这条通道</strong>：</p>
<ul>
<li class="">Firefox 141 彻底删除 CDP 支持；</li>
<li class="">ruyipage 走的是 W3C 标准的 <strong>WebDriver BiDi</strong>；</li>
<li class="">BiDi 的模块是 <code>browsingContext</code> / <code>script</code> / <code>input</code> / <code>network</code> / <code>storage</code> 等，
<strong>没有 <code>Debugger.enable</code></strong>。</li>
</ul>
<p>所以页面里的 <code>debugger;</code> 仍然是无害的空操作，计时和调用栈都正常。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="83-ruyipage-的额外好处">8.3 ruyipage 的额外好处<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#83-ruyipage-%E7%9A%84%E9%A2%9D%E5%A4%96%E5%A5%BD%E5%A4%84" class="hash-link" aria-label="8.3 ruyipage 的额外好处的直接链接" title="8.3 ruyipage 的额外好处的直接链接" translate="no">​</a></h3>
<ul>
<li class="">自带 Firefox 内核，<code>navigator.webdriver</code> 恒为 <code>false</code>；</li>
<li class="">输入走浏览器原生输入层，点击/键盘/拖拽是 <code>isTrusted</code> 事件；</li>
<li class="">支持元素截图、<code>localStorage</code> 读写、多标签页、网络拦截；</li>
<li class="">API 风格接近 DrissionPage，迁移成本低。</li>
</ul>
<blockquote>
<p>如果你不想引入 ruyipage，理论上 Selenium + Firefox 也能避开 CDP Debugger，
但元素截图、BiDi 输入、多标签页管理都要自己补，工程成本更高。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="九环境准备">九、环境准备<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%B9%9D%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87" class="hash-link" aria-label="九、环境准备的直接链接" title="九、环境准备的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="91-python-版本">9.1 Python 版本<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#91-python-%E7%89%88%E6%9C%AC" class="hash-link" aria-label="9.1 Python 版本的直接链接" title="9.1 Python 版本的直接链接" translate="no">​</a></h3>
<p>ruyipage 要求 <strong>Python &gt;= 3.9</strong>。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">python </span><span class="token parameter variable" style="color:#36acaa">-V</span><br></div></code></pre></div></div>
<p>如果是 Ubuntu 20.04 自带的 3.8，<code>pip install</code> 会直接报：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">ERROR: Package 'ruyipage' requires a different Python: 3.8.10 not in '&gt;=3.9'</span><br></div></code></pre></div></div>
<p><strong>不要</strong>用 <code>--ignore-requires-python</code> 硬装。ruyipage 里大量使用 3.9+ 的运行时语法
（<code>list[str]</code> 这类注解在 3.8 里会直接抛
<code>TypeError: 'type' object is not subscriptable</code>），必须换 Python。</p>
<p>内网服务器没法装 Python 时，可以用 python-build-standalone 的独立
Python 3.11（解压即用，不需要 root）：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> /opt/python3.11</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">tar</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-xzf</span><span class="token plain"> cpython-3.11.17+*-x86_64-unknown-linux-gnu-install_only.tar.gz </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-C</span><span class="token plain"> /opt/python3.11 --strip-components</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/opt/python3.11/bin/python3.11 </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> venv venv311</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">source</span><span class="token plain"> venv311/bin/activate</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="92-安装-ruyipage">9.2 安装 ruyipage<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#92-%E5%AE%89%E8%A3%85-ruyipage" class="hash-link" aria-label="9.2 安装 ruyipage的直接链接" title="9.2 安装 ruyipage的直接链接" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> ruyiPage</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> ruyipage </span><span class="token function" style="color:#d73a49">install</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># 下载配套 Firefox 内核（约 46MB 压缩 / 700MB 解压）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> ruyipage doctor       </span><span class="token comment" style="color:#999988;font-style:italic"># 检查是否就绪</span><br></div></code></pre></div></div>
<p><code>doctor</code> 正常输出类似：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">version: 158.0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">platform: linux-x86_64</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">path: /home/xxx/.cache/ruyipage/browsers/firefox-158.0-.../firefox/firefox</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="93-已经有系统-firefox">9.3 已经有系统 Firefox？<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#93-%E5%B7%B2%E7%BB%8F%E6%9C%89%E7%B3%BB%E7%BB%9F-firefox" class="hash-link" aria-label="9.3 已经有系统 Firefox？的直接链接" title="9.3 已经有系统 Firefox？的直接链接" translate="no">​</a></h3>
<p>可以不装自带内核。ruyipage 的查找顺序是：</p>
<ol>
<li class=""><code>launch(browser_path=...)</code> 显式指定</li>
<li class="">环境变量 <code>RUYIPAGE_FIREFOX_EXECUTABLE_PATH</code> / <code>RUYIPAGE_BROWSER_PATH</code></li>
<li class="">ruyipage 自带内核</li>
<li class="">系统 Firefox（<code>/usr/bin/firefox</code>、<code>/usr/local/bin/firefox</code>、<code>/snap/bin/firefox</code>）</li>
</ol>
<p>建议 Firefox <strong>&gt;= 130</strong>（BiDi 命令才完整）。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">firefox </span><span class="token parameter variable" style="color:#36acaa">--version</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RUYIPAGE_FIREFOX_EXECUTABLE_PATH</span><span class="token operator" style="color:#393A34">=</span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable function" style="color:#d73a49">which</span><span class="token variable" style="color:#36acaa"> firefox</span><span class="token variable" style="color:#36acaa">)</span><br></div></code></pre></div></div>
<p>Ubuntu 的 <strong>snap 版 Firefox</strong> 只能访问 <code>$HOME</code>，如果 profile 放在 <code>/opt</code>
会启动失败。把 profile 移到 home 下即可：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token builtin class-name">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RUYIPAGE_PROFILE</span><span class="token operator" style="color:#393A34">=</span><span class="token environment constant" style="color:#36acaa">$HOME</span><span class="token plain">/xxx_firefox</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="94-linux-依赖与字体">9.4 Linux 依赖与字体<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#94-linux-%E4%BE%9D%E8%B5%96%E4%B8%8E%E5%AD%97%E4%BD%93" class="hash-link" aria-label="9.4 Linux 依赖与字体的直接链接" title="9.4 Linux 依赖与字体的直接链接" translate="no">​</a></h3>
<p>Firefox 精简内核在服务器上可能缺库：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  libgtk-3-0 libdbus-glib-1-2 libxt6 libasound2 libx11-xcb1 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  libnss3 libnspr4 libpango-1.0-0 libcairo2 libatk1.0-0 libatk-bridge2.0-0 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  libatspi2.0-0 libxkbcommon0 libdrm2 libxshmfence1 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  fonts-noto-cjk fonts-wqy-zenhei</span><br></div></code></pre></div></div>
<p>中文字体很重要：没有 CJK 字体时页面能开，但截图里中文会是方块。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="95-图形环境">9.5 图形环境<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#95-%E5%9B%BE%E5%BD%A2%E7%8E%AF%E5%A2%83" class="hash-link" aria-label="9.5 图形环境的直接链接" title="9.5 图形环境的直接链接" translate="no">​</a></h3>
<p>不要用 headless（更容易被风控，部分站点还会渲染异常）。服务器没有桌面时用 Xvfb：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"> xvfb</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">xvfb-run </span><span class="token parameter variable" style="color:#36acaa">-a</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-s</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"-screen 0 1920x1080x24"</span><span class="token plain"> python demo.py</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十最小验证10-行代码确认能不能过">十、最小验证：10 行代码确认能不能过<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E6%9C%80%E5%B0%8F%E9%AA%8C%E8%AF%8110-%E8%A1%8C%E4%BB%A3%E7%A0%81%E7%A1%AE%E8%AE%A4%E8%83%BD%E4%B8%8D%E8%83%BD%E8%BF%87" class="hash-link" aria-label="十、最小验证：10 行代码确认能不能过的直接链接" title="十、最小验证：10 行代码确认能不能过的直接链接" translate="no">​</a></h2>
<p>别急着改业务代码。先跑通「打开页面是 200」：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> ruyipage </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> launch</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    user_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"./xxx_firefox"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    headless</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    close_on_exit</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    window_size</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1920</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1080</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://xxx.example.com/search"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> wait</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"interactive"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"title ="</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">title</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"html  ="</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">html </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text  ="</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">html </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">200</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">quit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>判读：</p>
<table><thead><tr><th>输出</th><th>说明</th></tr></thead><tbody><tr><td>标题正常、HTML 几万字符</td><td>✅ 挑战已过，可以继续</td></tr><tr><td>空白 / 400</td><td>先确认没有装会开 Debugger 的扩展，或换自带内核</td></tr><tr><td>跳到登录页</td><td>正常，需要走登录流程</td></tr></tbody></table>
<blockquote>
<p><strong><code>wait="interactive"</code> 很关键</strong>：只等 <code>DOMContentLoaded</code>，不等图片。
目标站图片慢时，等 <code>complete</code> 会让 <code>browsingContext.navigate</code> 卡满 30 秒并报超时，
页面其实早就可用了。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十一工程化把旧代码迁到-ruyipage">十一、工程化：把旧代码迁到 ruyipage<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B8%80%E5%B7%A5%E7%A8%8B%E5%8C%96%E6%8A%8A%E6%97%A7%E4%BB%A3%E7%A0%81%E8%BF%81%E5%88%B0-ruyipage" class="hash-link" aria-label="十一、工程化：把旧代码迁到 ruyipage的直接链接" title="十一、工程化：把旧代码迁到 ruyipage的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="111-为什么需要适配层">11.1 为什么需要适配层<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#111-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E9%80%82%E9%85%8D%E5%B1%82" class="hash-link" aria-label="11.1 为什么需要适配层的直接链接" title="11.1 为什么需要适配层的直接链接" translate="no">​</a></h3>
<p>如果你原来的代码是按 DrissionPage 写的，会大量出现这种选择器：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".btn operation"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># class 同时包含 btn 和 operation</span><br></div></code></pre></div></div>
<p>DrissionPage 的 <code>.</code> 表示「class 属性包含」，ruyipage 的 <code>.</code> 是标准 CSS。
直接传会被解析成「<code>.btn</code> 下面的 <code>&lt;operation&gt;</code> 标签」，永远选不中。</p>
<p>适配层要做四件事：</p>
<ol>
<li class=""><strong>选择器翻译</strong>：<code>.btn operation</code> → <code>css:.btn.operation</code></li>
<li class=""><strong>方法对齐</strong>：<code>click()</code> → <code>click_self()</code>，<code>get_screenshot()</code> → <code>screenshot()</code></li>
<li class=""><strong>动作链对齐</strong>：DP 的 <code>hold/move/release</code> 是逐条立即执行，
ruyipage 是排队后统一 <code>perform()</code></li>
<li class=""><strong>JS 与按键对齐</strong>：元素级 <code>run_js</code> 要包成函数；<code>input("...\n")</code> 要转成 <code>Keys.ENTER</code></li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="112-适配层代码可直接用">11.2 适配层代码（可直接用）<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#112-%E9%80%82%E9%85%8D%E5%B1%82%E4%BB%A3%E7%A0%81%E5%8F%AF%E7%9B%B4%E6%8E%A5%E7%94%A8" class="hash-link" aria-label="11.2 适配层代码（可直接用）的直接链接" title="11.2 适配层代码（可直接用）的直接链接" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># -*- coding: utf-8 -*-</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token triple-quoted-string string" style="color:#e3116c">"""DrissionPage 风格 -&gt; ruyipage 的最小适配层"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> time</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> ruyipage </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Keys</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token builtin">isinstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> sel</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> sel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"c:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"xpath:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"x:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"text:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"text="</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tag:"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"#"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"["</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"*"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"css:"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        words </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">w </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> w </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> w</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> words</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"css:body"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token builtin">all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> w</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> w </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> words</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"css:"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"."</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> w </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> w </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> words</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"css:."</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">" "</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">words</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Actions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> owner</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> owner</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">hold</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ele</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hold</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">getattr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"_el"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ele</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">perform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> offset_x</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> offset_y</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> duration</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> duration </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># DP 传秒，ruyipage 要毫秒</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            duration </span><span class="token operator" style="color:#393A34">*=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">offset_x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">offset_y</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">duration</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">perform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">release</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">release</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">perform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Element</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> el</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> el</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__bool__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">html</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">html </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">attr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">attr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">style</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rect</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click_self</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> clear</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        enter </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">endswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\n"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        body </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rstrip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\r\n"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> body</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">body</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> clear</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">clear</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">elif</span><span class="token plain"> clear</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">pass</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> enter</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_owner</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">press</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Keys</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ENTER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">perform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">clear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">pass</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> js</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        js </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">js</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> js</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"("</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"async"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">js</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"function(){"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> js </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"}"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Element</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timeout</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">Element</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> e </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timeout</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> as_bytes</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"png"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">as_bytes</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">as_bytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Tab</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> obj</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> obj</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tab_id</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tab_id</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">url</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">url</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">title</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">title</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">html</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">html </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">actions</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Actions</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> url</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> wait</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"interactive"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> wait</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">wait</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timeout</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Element</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timeout</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">Element</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> e </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">translate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timeout</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> js</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        js </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">js</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"return"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> js </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> js</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"("</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">js</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> as_expr</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">js</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> as_expr</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> as_bytes</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"png"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> full_page</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">full_page</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">full_page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> as_bytes</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">as_bytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Tab</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token decorator annotation punctuation" style="color:#393A34">@property</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">latest_tab</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Tab</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">latest_tab</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_tabs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">Tab</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_tabs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">quit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">quit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">_o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<blockquote>
<p>这段代码解决 90% 的迁移问题。剩下 10% 通常是「某个元素必须用 JS 点」
或「新标签页切换」，下面会讲。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十二登录oauth-授权码模式的坑">十二、登录：OAuth 授权码模式的坑<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%BA%8C%E7%99%BB%E5%BD%95oauth-%E6%8E%88%E6%9D%83%E7%A0%81%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%9D%91" class="hash-link" aria-label="十二、登录：OAuth 授权码模式的坑的直接链接" title="十二、登录：OAuth 授权码模式的坑的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="121-不要直接打开登录页">12.1 不要直接打开登录页<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#121-%E4%B8%8D%E8%A6%81%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%E7%99%BB%E5%BD%95%E9%A1%B5" class="hash-link" aria-label="12.1 不要直接打开登录页的直接链接" title="12.1 不要直接打开登录页的直接链接" translate="no">​</a></h3>
<p>很多站点的登录是「统一身份认证平台」，业务流程是 OAuth 授权码：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">业务站 /oauth/authorize?client_id=xxx&amp;redirect_uri=业务站/init</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ↓ 未登录</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">统一登录页 → 账号密码 + 验证码</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ↓ 登录成功</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">业务站 /init?code=xxxx</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ↓ 前端用 code 换 token</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">localStorage.setItem("token", jwt)</span><br></div></code></pre></div></div>
<p>如果你直接打开「统一登录页」，登录成功后只会回到<strong>统一身份平台首页</strong>，
业务站仍然是未登录状态（页头还挂着「登录/注册」，<code>localStorage.token</code> 为空）。</p>
<p>正确做法：<strong>从业务站的 <code>authorize</code> 地址进入</strong>，让 OAuth 流程自己把 <code>code</code> 带回来。</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">AUTH_URL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"https://xxx.example.com/oauth/authorize"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"?response_type=code&amp;scope=openid"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"&amp;client_id=xxx"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"&amp;redirect_uri=https://xxx.example.com/init"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">AUTH_URL</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="122-登录态怎么判断">12.2 登录态怎么判断<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#122-%E7%99%BB%E5%BD%95%E6%80%81%E6%80%8E%E4%B9%88%E5%88%A4%E6%96%AD" class="hash-link" aria-label="12.2 登录态怎么判断的直接链接" title="12.2 登录态怎么判断的直接链接" translate="no">​</a></h3>
<p>不要只看 URL。可靠的三件套：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">logged_in</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 1) 不在登录页</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"login"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">lower</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 2) 页头没有「登录/注册」入口</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.login-entry"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 3) localStorage 里有 token（或页面上有用户菜单）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'return localStorage.getItem("token")'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.user-menu"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<blockquote>
<p>只判断 URL 很容易误判：很多 SPA 在页面还没渲染完时 URL 就已经是首页了。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="123-滑块验证码">12.3 滑块验证码<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#123-%E6%BB%91%E5%9D%97%E9%AA%8C%E8%AF%81%E7%A0%81" class="hash-link" aria-label="12.3 滑块验证码的直接链接" title="12.3 滑块验证码的直接链接" translate="no">​</a></h3>
<p>滑块通用结构：</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">verify-img-panel</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">img</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">src</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">data:image/png;base64,...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">verify-sub-block</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">img</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">src</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">data:image/png;base64,...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">verify-move-block</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>识别思路：</p>
<ol>
<li class="">两张图都是 data URI，直接 base64 解码；</li>
<li class="">滑块图经常是 RGBA，带透明通道——<strong>用 alpha 当掩码</strong>；</li>
<li class="">用 <code>TM_CCORR_NORMED</code> + 掩码做模板匹配，找缺口 x；</li>
<li class="">缩放比 = 显示宽度 / <code>naturalWidth</code>，拖拽距离要乘这个比例。</li>
</ol>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> base64</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> io </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> BytesIO</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> PIL </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Image</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">b64</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">","</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> b64</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        b64 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> b64</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">","</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Image</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">BytesIO</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">base64</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">b64decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">b64</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    alpha </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">img</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> img</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mode </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"RGBA"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"LA"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    rgb </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cvtColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">img</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">convert</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"RGB"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">COLOR_RGB2BGR</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> rgb</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> alpha</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">find_gap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> alpha</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    bg_gray </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cvtColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">COLOR_BGR2GRAY</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sl_gray </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cvtColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">COLOR_BGR2GRAY</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> alpha </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> alpha</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> sl_gray</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        mask </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">alpha </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">astype</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uint8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> mask</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            res </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">matchTemplate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg_gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sl_gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">TM_CCORR_NORMED</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mask</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            res </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">matchTemplate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg_gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sl_gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">TM_CCOEFF_NORMED</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        res </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">matchTemplate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg_gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sl_gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">TM_CCOEFF_NORMED</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    res </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nan_to_num</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> nan</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> posinf</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> neginf</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    _</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> score</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">minMaxLoc</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> loc</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> score</span><br></div></code></pre></div></div>
<p>拖拽要点：</p>
<ul>
<li class="">轨迹要有中间步骤，先加速后减速，总和严格等于目标距离；</li>
<li class="">最后轻微滑过头再回正，更像人手；</li>
<li class=""><strong>成功判断不要只看页面有没有「验证成功」字样</strong>，很多站点成功后直接收起面板，
以 <code>.verify-img-panel img</code> 消失为准。</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">solve_slider</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    bg_el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.verify-img-panel img"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sl_el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.verify-sub-block img"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> bg_el </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> sl_el</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    bg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">attr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"src"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> alpha </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sl_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">attr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"src"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gap_x</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> score </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> find_gap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> alpha</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ratio </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        w </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"[^0-9.]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> bg_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"width"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        natural </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bg_el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"return this.naturalWidth"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> w </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> natural</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            ratio </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> w </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> natural</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">pass</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    distance </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gap_x </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> ratio</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    n </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    weights </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.25</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3.0</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">i </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> n</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.8</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> i </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    total </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">weights</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tracks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">distance </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> w </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> total</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> w </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> weights</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tracks</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">distance</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tracks</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    handle </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".verify-move-block"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> handle</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hold</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handle</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> tracks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">offset_x</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> offset_y</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">randint</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> duration</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.01</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">offset_x</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> duration</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">move</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">offset_x</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> duration</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">actions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">release</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"验证成功"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">html </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"验证通过"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">html </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.verify-img-panel img"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十三检索详情与截图">十三、检索、详情与截图<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B8%89%E6%A3%80%E7%B4%A2%E8%AF%A6%E6%83%85%E4%B8%8E%E6%88%AA%E5%9B%BE" class="hash-link" aria-label="十三、检索、详情与截图的直接链接" title="十三、检索、详情与截图的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="131-只等-dom不等图片">13.1 只等 DOM，不等图片<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#131-%E5%8F%AA%E7%AD%89-dom%E4%B8%8D%E7%AD%89%E5%9B%BE%E7%89%87" class="hash-link" aria-label="13.1 只等 DOM，不等图片的直接链接" title="13.1 只等 DOM，不等图片的直接链接" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">open_search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">SEARCH_URL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> wait</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"interactive"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".search-input"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> box</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">raise</span><span class="token plain"> RuntimeError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"检索框没出来"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> box</span><br></div></code></pre></div></div>
<p>如果你看到：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">导航超时: https://xxx.example.com/search -&gt; 命令超时: browsingContext.navigate (30s)</span><br></div></code></pre></div></div>
<p>说明还在用 <code>wait="complete"</code>。改成 <code>interactive</code>，页面 DOM 一好就继续，
后面的元素查找有自己的超时兜底，不会卡死。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="132-提交检索并等新标签页">13.2 提交检索并等新标签页<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#132-%E6%8F%90%E4%BA%A4%E6%A3%80%E7%B4%A2%E5%B9%B6%E7%AD%89%E6%96%B0%E6%A0%87%E7%AD%BE%E9%A1%B5" class="hash-link" aria-label="13.2 提交检索并等新标签页的直接链接" title="13.2 提交检索并等新标签页的直接链接" translate="no">​</a></h3>
<p>很多站点的结果页是新开标签：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> keyword</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> open_search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    inp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> box</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"tag:input"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    inp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    inp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keyword </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\n"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic"># 适配层会把 \n 转成 Enter</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> _ </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">15</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        tab </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">latest_tab</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"login"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">lower</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"NEED_LOGIN"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".result-row"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> tab</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="133-打开详情">13.3 打开详情<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#133-%E6%89%93%E5%BC%80%E8%AF%A6%E6%83%85" class="hash-link" aria-label="13.3 打开详情的直接链接" title="13.3 打开详情的直接链接" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">open_detail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result_tab</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">30</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    before </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tab_id </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_tabs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    result_tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".result-row"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    deadline </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> timeout</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> deadline</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_tabs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tab_id </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> before</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">continue</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".detail-panel"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> t</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="134-字段解析">13.4 字段解析<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#134-%E5%AD%97%E6%AE%B5%E8%A7%A3%E6%9E%90" class="hash-link" aria-label="13.4 字段解析的直接链接" title="13.4 字段解析的直接链接" translate="no">​</a></h3>
<p>服务端渲染的详情页直接用 BeautifulSoup，比在浏览器里逐个 <code>ele()</code> 快得多：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> bs4 </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> BeautifulSoup</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parse_detail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">html</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    soup </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> BeautifulSoup</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">html</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"html.parser"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    root </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> soup</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_one</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".detail-panel"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> root</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> item </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> root</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".kv-item"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        k </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_one</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        v </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_one</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".value"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> k </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> v</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">k</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">strip</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> v</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">strip</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> data</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="135-元素截图">13.5 元素截图<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#135-%E5%85%83%E7%B4%A0%E6%88%AA%E5%9B%BE" class="hash-link" aria-label="13.5 元素截图的直接链接" title="13.5 元素截图的直接链接" translate="no">​</a></h3>
<p>只截内容区，不要整页截（整页会把浏览器标签栏、账号信息一起截进去）：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tab</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".result-panel"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> el</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> base64</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">b64encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"utf-8"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> base64</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">b64encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">full_page</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"utf-8"</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十四完整代码">十四、完整代码<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E5%9B%9B%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%81" class="hash-link" aria-label="十四、完整代码的直接链接" title="十四、完整代码的直接链接" translate="no">​</a></h2>
<p>把上面的片段串起来（<code>demo.py</code>）。</p>
<blockquote>
<p>⚠️ 这是「拼装版」：运行前需要把 <strong>第十一节的适配层</strong>（<code>translate</code> / <code>Actions</code> / <code>Element</code> / <code>Tab</code> / <code>Page</code>）和 <strong>12.3 的滑块函数</strong>（<code>decode</code> / <code>find_gap</code> / <code>solve_slider</code>）一起复制到同一个文件。为了不让文章过长，这里不再重复贴。</p>
</blockquote>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># -*- coding: utf-8 -*-</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> argparse</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> base64</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> os</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> time</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> io </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> BytesIO</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> bs4 </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> BeautifulSoup</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> PIL </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Image</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> ruyipage </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Keys</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> launch</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">BASE_URL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://xxx.example.com"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">SEARCH_URL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> BASE_URL </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/search"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">AUTH_URL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">BASE_URL </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/oauth/authorize?response_type=code&amp;scope=openid"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"&amp;client_id=xxx&amp;redirect_uri="</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> BASE_URL </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/init"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACCOUNT </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"XXX_USER"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">PASSWORD </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"XXX_PASS"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># ---------- 依赖（从上面复制进来） ----------</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 1) 第十一节的 translate / Actions / Element / Tab / Page</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 2) 12.3 的 decode / find_gap / solve_slider</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parse_detail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">html</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    soup </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> BeautifulSoup</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">html</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"html.parser"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    root </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> soup</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_one</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".detail-panel"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> root</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> item </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> root</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".kv-item"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        k </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_one</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        v </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">select_one</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".value"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> k </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> v</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">k</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">strip</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> v</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">strip</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">handle_consent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""点掉免责声明 / 协议页"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> word</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_len </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"记住我的选择"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"同意"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">12</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            els </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string-interpolation string" style="color:#e3116c">f'xpath://*[contains(text(),"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">word</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">") and string-length(text())&lt;</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">max_len</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">]'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            els </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> el </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> els</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            txt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> txt </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token builtin">any</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">d </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> txt </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> d </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"不同意"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"拒绝"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"取消"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">continue</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">continue</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">logged_in</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"login"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">lower</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.login-entry"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'return localStorage.getItem("token")'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">pass</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.search-input"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">login</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> ACCOUNT </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">raise</span><span class="token plain"> SystemExit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"请设置 XXX_USER / XXX_PASS"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">AUTH_URL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    handle_consent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    inputs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.login-form input"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> page</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> box </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">enumerate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">inputs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        inp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> box</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"tag:input"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            inp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">clear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">pass</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        inp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">ACCOUNT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> PASSWORD</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".login-btn"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> i </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        handle_consent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css:.verify-img-panel img"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">break</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"滑块第 </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">i</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> 次"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            solve_slider</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"滑块异常:"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> _ </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">30</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run_js</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'return localStorage.getItem("token")'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"登录成功"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">break</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> page</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ensure_login</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> force</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">SEARCH_URL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> force </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> logged_in</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> page</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> attempt </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"第 </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">attempt</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> 次登录"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        login</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        handle_consent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">SEARCH_URL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> logged_in</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> page</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> page</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">read_detail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result_tab</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    st </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        panel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> result_tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".tab-list"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> panel</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            panel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text:法律状态"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> row </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> result_tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".table-row"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            st </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> row</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"xpath:.//td[3]"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> st</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">break</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> Exception</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">pass</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> result_tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".result-panel"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> el</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> base64</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">b64encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">screenshot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"utf-8"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    before </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tab_id </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_tabs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    result_tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eles</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".result-row"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">click</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    detail_tab </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    deadline </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> deadline</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_tabs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tab_id </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> before </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ele</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">".detail-panel"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                detail_tab </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> t</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">break</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> detail_tab</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">break</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    fields </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"法律状态"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> st</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> st </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> detail_tab</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> _ </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            detail </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> parse_detail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">detail_tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">html </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> detail</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                fields</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">detail</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">break</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        detail_tab</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> fields</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> img</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> argparse</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ArgumentParser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">add_argument</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"--kw"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> required</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">add_argument</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"--json"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> action</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"store_true"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ap</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">parse_args</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">launch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        user_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"./xxx_firefox"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        headless</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        close_on_exit</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        window_size</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1920</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1080</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        timeout_base</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">15</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        timeout_page_load</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        page </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ensure_login</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        result_tab </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">kw</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token builtin">isinstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result_tab</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> result_tab </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"无结果 / 需要登录"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        fields</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> read_detail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result_tab</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        out </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"found"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">fields</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fields"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> fields</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"img_len"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">img </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ensure_ascii</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> indent</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> k</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> v </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> fields</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">items</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">k</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> = </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">v</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">finally</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">quit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"__main__"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<blockquote>
<p>说明：选择器（<code>.search-input</code>、<code>.result-row</code>、<code>.detail-panel</code> 等）都是占位示例，
换成你自己站点的即可，<strong>逻辑和坑位是通用的</strong>。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十五踩坑合集">十五、踩坑合集<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%BA%94%E8%B8%A9%E5%9D%91%E5%90%88%E9%9B%86" class="hash-link" aria-label="十五、踩坑合集的直接链接" title="十五、踩坑合集的直接链接" translate="no">​</a></h2>
<table><thead><tr><th>坑</th><th>现象</th><th>解决</th></tr></thead><tbody><tr><td>导航 30 秒超时</td><td><code>browsingContext.navigate (30s)</code>，但页面其实已加载</td><td><code>wait="interactive"</code>，别用 <code>complete</code></td></tr><tr><td>滑块总失败</td><td>匹配度很高但过不了</td><td>确认缩放比方向（显示/原图）；成功以面板消失为准</td></tr><tr><td>纯色拼图匹配错位</td><td>缺口 x 偏到背景平坦区</td><td>用 alpha 掩码 + <code>TM_CCORR_NORMED</code>，或白色描边轮廓</td></tr><tr><td>登录了但业务站未登录</td><td>URL 是登录页/门户首页，token 为空</td><td>从业务站 OAuth authorize 入口进，不要直开统一登录页</td></tr><tr><td>token 突然消失</td><td>偶发 400 后变成未登录</td><td>接口报错时前端会跳 <code>/init</code> 并删 token，重登即可</td></tr><tr><td>整站 400</td><td>所有请求都 400，停一会儿又好了</td><td>频率规则，退避 30~60 秒，别 2 秒猛刷</td></tr><tr><td><code>Enter</code> 报错</td><td><code>Expected "value" to be a single code point</code></td><td>用 <code>Keys.ENTER</code>，不要传字符串 <code>"Enter"</code></td></tr><tr><td>元素 <code>run_js</code> 报错</td><td>传了 <code>this.value=''</code> 这种语句</td><td>ruyipage 需要函数声明，包成 <code>function(){...}</code></td></tr><tr><td>选择器选不中</td><td><code>.btn operation</code> 被当 CSS 后代</td><td>翻译成 <code>.btn.operation</code></td></tr><tr><td>Python 3.8 装不上</td><td><code>requires a different Python</code></td><td>用 3.9+（内网可用独立 3.11）</td></tr><tr><td>snap Firefox 起不来</td><td>profile 在 <code>/opt</code> 被拒绝</td><td>profile 放 <code>$HOME</code></td></tr><tr><td>profile 被锁</td><td>第二个进程启动失败</td><td>同一 profile 只允许一个进程</td></tr><tr><td>无中文字体</td><td>截图中文变方块</td><td>装 <code>fonts-noto-cjk</code></td></tr><tr><td>无图形环境</td><td><code>Cannot open display</code></td><td><code>xvfb-run</code> 或 VNC</td></tr><tr><td>F12 调试就 400</td><td>开了 DevTools</td><td>自动化窗口里不要开 F12</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十六部署到-linux-服务器">十六、部署到 Linux 服务器<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E5%85%AD%E9%83%A8%E7%BD%B2%E5%88%B0-linux-%E6%9C%8D%E5%8A%A1%E5%99%A8" class="hash-link" aria-label="十六、部署到 Linux 服务器的直接链接" title="十六、部署到 Linux 服务器的直接链接" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 1) 独立虚拟环境</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python3.11 </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> venv venv</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">source</span><span class="token plain"> venv/bin/activate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> ruyiPage opencv-python numpy Pillow beautifulsoup4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> ruyipage </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> ruyipage doctor</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 2) 有桌面：直接跑</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python demo.py </span><span class="token parameter variable" style="color:#36acaa">--kw</span><span class="token plain"> xxx</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 3) 无桌面：Xvfb</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">xvfb-run </span><span class="token parameter variable" style="color:#36acaa">-a</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-s</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"-screen 0 1920x1080x24"</span><span class="token plain"> python demo.py </span><span class="token parameter variable" style="color:#36acaa">--kw</span><span class="token plain"> xxx</span><br></div></code></pre></div></div>
<p>systemd 示例（无桌面）：</p>
<div class="language-ini codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ini codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">[Unit]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Description=xxx crawler</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">After=network-online.target</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[Service]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Type=simple</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">User=app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">WorkingDirectory=/opt/app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EnvironmentFile=/opt/app/app.env</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ExecStart=/usr/bin/xvfb-run -a -s "-screen 0 1920x1080x24" \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  /opt/app/venv/bin/python /opt/app/demo.py --kw xxx</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Restart=always</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">RestartSec=15</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[Install]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">WantedBy=multi-user.target</span><br></div></code></pre></div></div>
<p><strong>完全离线</strong>的服务器：在能上网的机器上准备好</p>
<ul>
<li class=""><code>ruyipage</code> 及依赖的 wheel（纯 Python）</li>
<li class="">Firefox 内核 <code>.tar.xz</code>（<code>python -m ruyipage install --from-file ...</code>）</li>
<li class="">或者干脆用系统 Firefox，跳过内核</li>
</ul>
<p>拷过去后：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> --no-index --find-links</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">./wheels ruyipage</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> ruyipage </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> --from-file ./firefox-xxx.tar.xz</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十七faq">十七、FAQ<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B8%83faq" class="hash-link" aria-label="十七、FAQ的直接链接" title="十七、FAQ的直接链接" translate="no">​</a></h2>
<p><strong>Q1：能不能用 Selenium + Firefox？</strong></p>
<p>理论上可以，Selenium 的 Firefox 驱动走 WebDriver，不依赖 CDP Debugger。
但元素截图、BiDi 输入、多标签页、<code>localStorage</code> 读写都要自己补，
而且不同版本 Firefox 的 BiDi 支持差异较大。ruyipage 相当于把这些封装好了。</p>
<p><strong>Q2：能不能 headless？</strong></p>
<p>不建议。无头模式更容易被风控，部分站点渲染也不完整。
服务器上用 Xvfb 提供一个「有头」的虚拟显示更稳。</p>
<p><strong>Q3：能不能多线程/多进程？</strong></p>
<p>同一个 Firefox profile 只能被一个进程使用。要多并发就多开 profile、
多装实例，并注意目标站的频率限制。反爬站点通常不建议高并发。</p>
<p><strong>Q4：cookie 能不能导出给 requests 用？</strong></p>
<p>这类站点的 cookie 与浏览器 JS 环境绑定，导出回放大概率失效。
老老实实让浏览器发请求，或者只把浏览器当「取数通道」，不要试图全量 HTTP 化。</p>
<p><strong>Q5：为什么不用 undetected-chromedriver / stealth 插件？</strong></p>
<p>它们主要处理 <code>navigator.webdriver</code>、UA、指纹等「静态特征」，
但本文的问题是 <strong>Debugger 协议本身</strong>。只要 CDP Debugger 还开着，
再 stealth 也没用。</p>
<p><strong>Q6：还有哪些站点会这样？</strong></p>
<p>凡是「正常浏览器 200、Playwright/DP 400、F12 也 400」的站点，
都建议先做一次 Debugger 对照实验。瑞数、部分金融/政务站、
以及一些使用同类 JS 挑战的站点都可能命中。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十八总结">十八、总结<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E5%85%AB%E6%80%BB%E7%BB%93" class="hash-link" aria-label="十八、总结的直接链接" title="十八、总结的直接链接" translate="no">​</a></h2>
<ol>
<li class=""><strong>412/400 不一定是 cookie 或签名问题</strong>，先做「浏览器驱动方式」的对照实验。</li>
<li class="">最小 CDP 客户端 + 按域 enable，是最快定位手段。<strong>只有 Debugger 挂 = 石锤</strong>。</li>
<li class="">原理是 <code>debugger;</code> 的语义被调试器改变，计时/调用栈异常，挑战结果被静默作废。</li>
<li class="">解决思路不是「更努力地逆 JS」，而是<strong>换一个没有 Debugger 通道的协议</strong>：
Firefox + WebDriver BiDi。</li>
<li class="">工程落地注意四件事：选择器翻译、动作链逐条 <code>perform</code>、<code>Keys.ENTER</code>、
<code>wait="interactive"</code>。</li>
<li class="">最后，风控是「协议 + 指纹 + 频率 + 行为」的组合。协议过了，也别猛刷。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="十九合规声明">十九、合规声明<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E5%8D%81%E4%B9%9D%E5%90%88%E8%A7%84%E5%A3%B0%E6%98%8E" class="hash-link" aria-label="十九、合规声明的直接链接" title="十九、合规声明的直接链接" translate="no">​</a></h2>
<p>本文仅用于技术学习与合法的数据采集场景。请遵守目标站点的服务条款与
<code>robots.txt</code>，控制访问频率，不抓取需要授权的内容，不用于商业批量刷取。
因不当使用产生的后果由使用者自行承担。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二十配图建议">二十、配图建议<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/ruishu5#%E4%BA%8C%E5%8D%81%E9%85%8D%E5%9B%BE%E5%BB%BA%E8%AE%AE" class="hash-link" aria-label="二十、配图建议的直接链接" title="二十、配图建议的直接链接" translate="no">​</a></h2>
<p>发 CSDN 时建议配 4 张图（比纯文字更容易被推荐）：</p>
<ol>
<li class=""><strong>现象图</strong>：浏览器 Network 面板里 400 的响应体 <code>\r\n\r\n\r\n</code>（注意：用抓包工具截图，不要在自动化窗口开 F12）。</li>
<li class=""><strong>对照实验图</strong>：终端里 6 行输出，<code>Debugger -&gt; 400</code> 那一行用红框标出。</li>
<li class=""><strong>原理示意图</strong>：<code>普通浏览器 / Chrome+CDP / Firefox+BiDi</code> 三条链路对比。</li>
<li class=""><strong>成功图</strong>：脚本输出的 JSON（字段 + 截图长度）。</li>
</ol>
<blockquote>
<p>封面可以用「412 / 400 / Debugger」三个关键词做一张简洁的对比图。</p>
</blockquote>
<hr>
<p><strong>如果这篇文章帮你少走几天弯路，欢迎点赞、收藏、关注。</strong>
有问题可以在评论区留言，看到会回。</p>]]></content>
        <author>
            <name>佚名</name>
        </author>
        <category label="公告" term="公告"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Pi Agent 插件（Extension）开发入门教程]]></title>
        <id>https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial</id>
        <link href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial"/>
        <updated>2026-10-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[适用版本：pi 1.4.0]]></summary>
        <content type="html"><![CDATA[<blockquote>
<p>适用版本：pi 1.4.0
平台示例：Windows（命令在 macOS / Linux 下同样适用，路径换成 <code>~/.pi/agent</code>）
文档基线：<code>&lt;pi安装目录&gt;/docs/extensions.md</code>、<code>docs/tui.md</code></p>
</blockquote>
<p>本文从最小可运行插件讲起，覆盖加载机制、命令/工具/事件三种形态、TUI 自定义组件、状态持久化、调试排错与常见坑，最后给出一个完整可跑的示例。适合想给 Pi Agent 扩展能力但还没写过插件的同学。</p>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="目录">目录<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E7%9B%AE%E5%BD%95" class="hash-link" aria-label="目录的直接链接" title="目录的直接链接" translate="no">​</a></h2>
<ol>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#1-%E6%8F%92%E4%BB%B6%E6%98%AF%E4%BB%80%E4%B9%88" class="">插件是什么</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#2-%E6%9C%80%E5%B0%8F%E5%8F%AF%E8%BF%90%E8%A1%8C%E6%8F%92%E4%BB%B6" class="">最小可运行插件</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#3-%E5%8A%A0%E8%BD%BD%E6%96%B9%E5%BC%8F%E4%B8%8E%E7%9B%AE%E5%BD%95%E7%BA%A6%E5%AE%9A" class="">加载方式与目录约定</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#4-%E4%B8%89%E7%A7%8D%E6%9C%80%E5%B8%B8%E8%A7%81%E7%9A%84%E6%8F%92%E4%BB%B6%E5%BD%A2%E6%80%81" class="">三种最常见的插件形态</a>
<ul>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#41-%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%96%9C%E6%9D%A0%E5%91%BD%E4%BB%A4" class="">4.1 加一个斜杠命令</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#42-%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%A8%A1%E5%9E%8B%E5%8F%AF%E8%B0%83%E7%94%A8%E7%9A%84%E5%B7%A5%E5%85%B7" class="">4.2 加一个模型可调用的工具</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#43-%E7%9B%91%E5%90%AC%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%BA%8B%E4%BB%B6" class="">4.3 监听生命周期事件</a></li>
</ul>
</li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#5-ui-%E4%BA%A4%E4%BA%92" class="">UI 交互</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#6-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%88%E7%AB%AF%E7%95%8C%E9%9D%A2tui-%E7%BB%84%E4%BB%B6" class="">自定义终端界面（TUI 组件）</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#7-%E7%8A%B6%E6%80%81%E4%B8%8E%E6%8C%81%E4%B9%85%E5%8C%96" class="">状态与持久化</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#8-%E8%B0%83%E8%AF%95%E4%B8%8E%E6%8E%92%E9%94%99" class="">调试与排错</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#9-%E5%B8%B8%E8%A7%81%E5%9D%91" class="">常见坑</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#10-%E8%BF%9B%E9%98%B6%E6%96%B9%E5%90%91" class="">进阶方向</a></li>
<li class=""><a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#11-%E9%80%9F%E6%9F%A5%E8%A1%A8" class="">速查表</a></li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-插件是什么">1. 插件是什么<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#1-%E6%8F%92%E4%BB%B6%E6%98%AF%E4%BB%80%E4%B9%88" class="hash-link" aria-label="1. 插件是什么的直接链接" title="1. 插件是什么的直接链接" translate="no">​</a></h2>
<p>Pi 的插件叫 <strong>Extension</strong>，是一个 TypeScript 模块。它默认导出一个工厂函数，接收 <code>ExtensionAPI</code>，在里面注册能力：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 在这里注册命令、工具、事件监听……</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>插件能做的事：</p>
<table><thead><tr><th>能力</th><th>方法</th></tr></thead><tbody><tr><td>增加 <code>/</code> 斜杠命令</td><td><code>pi.registerCommand()</code></td></tr><tr><td>增加模型可调用的工具</td><td><code>pi.registerTool()</code></td></tr><tr><td>增加快捷键 / CLI 参数</td><td><code>pi.registerShortcut()</code> / <code>pi.registerFlag()</code></td></tr><tr><td>监听 / 干预生命周期</td><td><code>pi.on()</code></td></tr><tr><td>读写会话、改变当前模型</td><td><code>pi</code> 上的会话控制方法</td></tr><tr><td>增加终端界面</td><td><code>ctx.ui</code> + 自定义组件</td></tr><tr><td>增加模型供应商</td><td><code>pi.registerProvider()</code></td></tr></tbody></table>
<blockquote>
<p>⚠️ <strong>安全提示</strong>：插件在 Pi 进程内运行，拥有和你相同的操作系统权限。它能读你的文件、看你的对话历史和凭证。<strong>只加载你信任来源的插件。</strong></p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-最小可运行插件">2. 最小可运行插件<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#2-%E6%9C%80%E5%B0%8F%E5%8F%AF%E8%BF%90%E8%A1%8C%E6%8F%92%E4%BB%B6" class="hash-link" aria-label="2. 最小可运行插件的直接链接" title="2. 最小可运行插件的直接链接" translate="no">​</a></h2>
<p>创建文件 <code>&lt;agent目录&gt;/extensions/hello.ts</code>：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ExtensionAPI </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-coding-agent"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"hello"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"打个招呼"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Hello, </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">name </span><span class="token template-string interpolation operator" style="color:#393A34">||</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation string" style="color:#e3116c">"world"</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">!</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"info"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Windows 下的实际路径：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">C:\Users\&lt;你的用户名&gt;\.pi\agent\extensions\hello.ts</span><br></div></code></pre></div></div>
<p>保存后启动 Pi，输入 <code>/hello</code> 即可看到效果。带参数则输入 <code>/hello 张三</code>。</p>
<p><strong>不需要编译</strong>——Pi 内部用 <code>jiti</code> 直接加载 TypeScript。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-加载方式与目录约定">3. 加载方式与目录约定<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#3-%E5%8A%A0%E8%BD%BD%E6%96%B9%E5%BC%8F%E4%B8%8E%E7%9B%AE%E5%BD%95%E7%BA%A6%E5%AE%9A" class="hash-link" aria-label="3. 加载方式与目录约定的直接链接" title="3. 加载方式与目录约定的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-三个加载位置">3.1 三个加载位置<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#31-%E4%B8%89%E4%B8%AA%E5%8A%A0%E8%BD%BD%E4%BD%8D%E7%BD%AE" class="hash-link" aria-label="3.1 三个加载位置的直接链接" title="3.1 三个加载位置的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>位置</th><th>作用范围</th><th>说明</th></tr></thead><tbody><tr><td><code>~/.pi/agent/extensions/</code></td><td>个人，所有项目</td><td>最常用</td></tr><tr><td><code>&lt;项目&gt;/.pi/extensions/</code></td><td>当前项目</td><td>需要 <strong>项目信任（project trust）</strong> 后才加载</td></tr><tr><td><code>--extension &lt;路径&gt;</code></td><td>仅本次运行</td><td>开发调试最方便</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-文件还是目录">3.2 文件还是目录<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#32-%E6%96%87%E4%BB%B6%E8%BF%98%E6%98%AF%E7%9B%AE%E5%BD%95" class="hash-link" aria-label="3.2 文件还是目录的直接链接" title="3.2 文件还是目录的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>单文件</strong>：<code>extensions/hello.ts</code> —— 适合小插件</li>
<li class=""><strong>目录</strong>：<code>extensions/my-plugin/index.ts</code> —— 适合多文件实现</li>
<li class="">依赖放在旁边的 <code>package.json</code> 里</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-开发时热加载">3.3 开发时热加载<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#33-%E5%BC%80%E5%8F%91%E6%97%B6%E7%83%AD%E5%8A%A0%E8%BD%BD" class="hash-link" aria-label="3.3 开发时热加载的直接链接" title="3.3 开发时热加载的直接链接" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pi </span><span class="token parameter variable" style="color:#36acaa">--extension</span><span class="token plain"> ./my-plugin.ts</span><br></div></code></pre></div></div>
<p>修改后，在 Pi 里执行 <code>/reload</code> 重新加载（键位、扩展、技能、模板、主题、上下文文件都会重载）。</p>
<blockquote>
<p><code>/reload</code> 会<strong>替换整个扩展运行时</strong>，所以 <code>await ctx.reload()</code> 之后的代码属于旧运行时，不要再使用旧的状态。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-三种最常见的插件形态">4. 三种最常见的插件形态<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#4-%E4%B8%89%E7%A7%8D%E6%9C%80%E5%B8%B8%E8%A7%81%E7%9A%84%E6%8F%92%E4%BB%B6%E5%BD%A2%E6%80%81" class="hash-link" aria-label="4. 三种最常见的插件形态的直接链接" title="4. 三种最常见的插件形态的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-加一个斜杠命令">4.1 加一个斜杠命令<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#41-%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%96%9C%E6%9D%A0%E5%91%BD%E4%BB%A4" class="hash-link" aria-label="4.1 加一个斜杠命令的直接链接" title="4.1 加一个斜杠命令的直接链接" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ExtensionAPI </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-coding-agent"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"greet"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"向指定的人问好"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 可选：参数自动补全</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">getArgumentCompletions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> names </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"alice"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"bob"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"carol"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> hits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> names</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">filter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> n</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">startsWith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> hits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> hits</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> value</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> n</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> label</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> n </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> who </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">trim</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">who</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"用法: /greet &lt;名字&gt;"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"warning"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">你好，</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">who</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">！</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"info"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>要点</strong></p>
<ul>
<li class=""><code>handler</code> 签名固定为 <code>(args: string, ctx: ExtensionCommandContext) =&gt; Promise&lt;void&gt;</code></li>
<li class=""><code>args</code> 是命令名之后的原始字符串，<strong>没有经过分词</strong>，需要自己 <code>trim()</code> / <code>split()</code></li>
<li class=""><code>ctx.ui.notify(message, "info" | "warning" | "error")</code></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-加一个模型可调用的工具">4.2 加一个模型可调用的工具<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#42-%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%A8%A1%E5%9E%8B%E5%8F%AF%E8%B0%83%E7%94%A8%E7%9A%84%E5%B7%A5%E5%85%B7" class="hash-link" aria-label="4.2 加一个模型可调用的工具的直接链接" title="4.2 加一个模型可调用的工具的直接链接" translate="no">​</a></h3>
<p>工具和命令不同：命令由<strong>你</strong>触发，工具由<strong>模型</strong>决定何时调用。</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Type </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-ai"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> defineTool</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">ExtensionAPI</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-coding-agent"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> wordCount </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">defineTool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"word_count"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  label</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Word Count"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"统计一段文本的单词数量"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// 模型靠这句话判断何时用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  parameters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Type</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Object</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Type</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"要统计的文本"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_toolCallId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _signal</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _onUpdate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> words </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">trim</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex char-set class-name" style="color:#36acaa">\s</span><span class="token regex regex-source language-regex quantifier number" style="color:#36acaa">+</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">filter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Boolean</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"text"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">单词数: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">words</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation">length</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      details</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> words</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// 结构化数据，可为 undefined</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerTool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">wordCount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>execute 的五个参数</strong></p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">toolCallId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> signal</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> onUpdate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<table><thead><tr><th>参数</th><th>用途</th></tr></thead><tbody><tr><td><code>toolCallId</code></td><td>本次调用 id</td></tr><tr><td><code>params</code></td><td>经过 schema 校验的参数</td></tr><tr><td><code>signal</code></td><td><code>AbortSignal</code>，长任务要响应取消</td></tr><tr><td><code>onUpdate</code></td><td>汇报中间进度</td></tr><tr><td><code>ctx</code></td><td><code>ExtensionToolContext</code>，可 <code>ctx.executeTool()</code> 调用其他工具</td></tr></tbody></table>
<p><strong>必须返回的结构</strong></p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"..."</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 模型看到的内容</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  details</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">any</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">                              </span><span class="token comment" style="color:#999988;font-style:italic">// 给 UI / 状态重建用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>要点</strong></p>
<ul>
<li class=""><code>description</code> 决定模型会不会用这个工具，写得具体些</li>
<li class="">用 <code>Type.Object()</code> 声明参数（TypeBox），<strong>不要手写 JSON Schema</strong></li>
<li class="">抛异常 = 工具失败；返回对象 = 成功（想返回"带数据的错误"用 <code>isError: true</code>）</li>
<li class="">文件读写类工具要用 <code>withFileMutationQueue()</code> 包住整个"读-改-写"</li>
<li class="">结果很大时要截断，并告诉模型完整内容在哪</li>
</ul>
<p>验证是否生效：让模型用一下，例如</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">用 word_count 工具统计 'hello world foo'，只报数字</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-监听生命周期事件">4.3 监听生命周期事件<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#43-%E7%9B%91%E5%90%AC%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%BA%8B%E4%BB%B6" class="hash-link" aria-label="4.3 监听生命周期事件的直接链接" title="4.3 监听生命周期事件的直接链接" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ExtensionAPI </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-coding-agent"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// agent_settled：Pi 真正空闲下来（不会再有重试/压缩/后续）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"agent_settled"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">setStatus</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-plugin"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"就绪"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// tool_call：可以拦截工具调用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"tool_call"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">toolName </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"bash"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">command</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">includes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"rm -rf"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">confirm</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"危险操作"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">command</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> block</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> reason</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"用户拒绝执行"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// session_start：会话启动时初始化</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"session_start"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"插件已加载"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"info"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>常用事件选择</strong></p>
<table><thead><tr><th>目的</th><th>事件</th></tr></thead><tbody><tr><td>检查 / 改写 / 拦截用户输入</td><td><code>input</code></td></tr><tr><td>每次运行前改提示词或工具集</td><td><code>before_agent_start</code></td></tr><tr><td>观察一次 agent 运行</td><td><code>agent_start</code> / <code>agent_end</code></td></tr><tr><td>模型回完一轮</td><td><code>turn_end</code></td></tr><tr><td>过滤发给模型的对话</td><td><code>context</code></td></tr><tr><td>授权 / 修改工具调用</td><td><code>tool_call</code></td></tr><tr><td>脱敏 / 改写工具结果</td><td><code>tool_result</code></td></tr><tr><td>Pi 彻底空闲（通知类）</td><td><code>agent_settled</code></td></tr><tr><td>初始化 / 释放资源</td><td><code>session_start</code> / <code>session_shutdown</code></td></tr></tbody></table>
<blockquote>
<p><strong>注意</strong>：<code>agent_end</code> 不是"最终空闲"信号——之后可能还有重试、恢复、压缩或排队的后续消息。要做通知请用 <code>agent_settled</code>。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-ui-交互">5. UI 交互<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#5-ui-%E4%BA%A4%E4%BA%92" class="hash-link" aria-label="5. UI 交互的直接链接" title="5. UI 交互的直接链接" translate="no">​</a></h2>
<p><code>ctx.ui</code> 提供现成的对话框，优先用它们，不要自己造轮子：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 选择</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> choice </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"选择环境"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"dev"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"staging"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"prod"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 确认</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> ok </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">confirm</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"确认删除？"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"此操作不可撤销"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 输入</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"项目名"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"例如 my-app"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 通知</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"完成"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"info"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 状态栏文字（传 undefined 清除）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">setStatus</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"build"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"构建中…"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 编辑器内容</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getEditorText</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">setEditorText</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"预填充内容"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 替换编辑器 / 页眉 / 页脚</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">setEditorComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tui</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> theme</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> keybindings</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">MyEditor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p><strong>必须先判断模式</strong>：插件也会在 RPC / JSON / print 模式下加载，这些模式没有完整 UI。</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mode </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tui"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"此功能需要交互模式"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>也可以用 <code>ctx.hasUI</code> 判断（覆盖交互模式 + RPC）。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-自定义终端界面tui-组件">6. 自定义终端界面（TUI 组件）<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#6-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%88%E7%AB%AF%E7%95%8C%E9%9D%A2tui-%E7%BB%84%E4%BB%B6" class="hash-link" aria-label="6. 自定义终端界面（TUI 组件）的直接链接" title="6. 自定义终端界面（TUI 组件）的直接链接" translate="no">​</a></h2>
<p>内置对话框不够用时，用 <code>ctx.ui.custom()</code> 接管一块区域。</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ExtensionAPI </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-coding-agent"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> truncateToWidth </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-tui"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"pick"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"自建选择器"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mode </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tui"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> choices </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"red"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"green"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"blue"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> picked </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token generic-function function" style="color:#d73a49">custom</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name builtin">string</span><span class="token generic-function generic class-name"> </span><span class="token generic-function generic class-name operator" style="color:#393A34">|</span><span class="token generic-function generic class-name"> </span><span class="token generic-function generic class-name keyword" style="color:#00009f">undefined</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tui</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> theme</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> keybindings</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> done</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> cachedWidth</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">undefined</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> cachedLines</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">undefined</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">invalidate</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            cachedWidth </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">undefined</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            cachedLines </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">undefined</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cachedLines </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> cachedWidth </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> width</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> cachedLines</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              cachedWidth </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> width</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              cachedLines </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"accent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"选择颜色"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token operator" style="color:#393A34">...</span><span class="token plain">choices</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                  i </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> index </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"accent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">❯ </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">c</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">  </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">c</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"dim"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"↑/↓ 移动 · Enter 确认 · Esc 取消"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">line</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">truncateToWidth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">line</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> width</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 必须按宽度截断</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> cachedLines</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">handleInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keybindings</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">matches</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tui.select.up"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">index </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> choices</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> choices</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keybindings</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">matches</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tui.select.down"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">index </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> choices</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keybindings</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">matches</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tui.select.confirm"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">done</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">choices</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keybindings</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">matches</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tui.select.cancel"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">done</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">undefined</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token function" style="color:#d73a49">invalidate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              tui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">requestRender</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            invalidate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> overlay</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// overlay: 浮在现有内容上</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">picked</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">选择了 </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">picked</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"info"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>四条硬规则</strong></p>
<ol>
<li class=""><strong>每行都不能超过传入的 <code>width</code></strong> —— 用 <code>truncateToWidth()</code> / <code>visibleWidth()</code>，不要用 <code>String.length</code>（中文、emoji、ANSI 转义都会算错）</li>
<li class=""><strong>状态变了要 <code>invalidate()</code> 并 <code>tui.requestRender()</code></strong></li>
<li class=""><strong>用 <code>keybindings.matches(data, "动作ID")</code></strong> 判断按键，不要硬编码 <code>\x1b[A</code></li>
<li class=""><strong>每次交互都新建组件实例</strong>，不要复用</li>
</ol>
<p><strong>主题色用法</strong></p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"accent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"文字"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic">// 前景色</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">bg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"selectedBg"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"背景"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic">// 背景色</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">style</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"文字"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> fg</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"accent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> bold</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 组合样式</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">appearance                        </span><span class="token comment" style="color:#999988;font-style:italic">// "dark" | "light"</span><br></div></code></pre></div></div>
<p>不要缓存带颜色转义字符串（除非 <code>invalidate()</code> 会重建）——主题切换后旧转义会残留。</p>
<p><strong>想改 Pi 的主编辑器？</strong> 继承 <code>CustomEditor</code>，这样应用快捷键和 agent 控制都保留。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-状态与持久化">7. 状态与持久化<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#7-%E7%8A%B6%E6%80%81%E4%B8%8E%E6%8C%81%E4%B9%85%E5%8C%96" class="hash-link" aria-label="7. 状态与持久化的直接链接" title="7. 状态与持久化的直接链接" translate="no">​</a></h2>
<p>按状态的性质选存储方式：</p>
<table><thead><tr><th>状态类型</th><th>存储方式</th></tr></thead><tbody><tr><td>跟随当前分支的工具状态</td><td>工具结果的 <code>details</code> 字段</td></tr><tr><td>长期保存但<strong>不</strong>发给模型</td><td><code>pi.appendEntry()</code></td></tr><tr><td>要存<strong>并</strong>发给模型的自定义内容</td><td><code>pi.sendMessage()</code></td></tr><tr><td>跨会话数据</td><td>自己写文件</td></tr></tbody></table>
<p>分支敏感的状态要在 <code>session_start</code> 时从 <code>ctx.sessionManager.getBranch()</code> 重建——不要遍历所有文件条目重建，因为被放弃的分支代表"另一条历史"。</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"session_start"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> branch </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sessionManager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getBranch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 从 branch 中恢复你的插件状态</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p><strong>清理资源</strong>：在 <code>session_shutdown</code> 里释放，并且要<strong>幂等</strong>——取消、重载、切换会话、进程退出都可能走到同一条清理路径。</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"session_shutdown"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">myTimer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<blockquote>
<p>不要在工厂函数里启动进程、socket、watcher 或定时器——有些调用不会启动会话。长生命周期资源从 <code>session_start</code> 或用到它的命令里启动。</p>
</blockquote>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-调试与排错">8. 调试与排错<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#8-%E8%B0%83%E8%AF%95%E4%B8%8E%E6%8E%92%E9%94%99" class="hash-link" aria-label="8. 调试与排错的直接链接" title="8. 调试与排错的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="81-开发循环">8.1 开发循环<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#81-%E5%BC%80%E5%8F%91%E5%BE%AA%E7%8E%AF" class="hash-link" aria-label="8.1 开发循环的直接链接" title="8.1 开发循环的直接链接" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pi </span><span class="token parameter variable" style="color:#36acaa">--extension</span><span class="token plain"> ./my-plugin.ts</span><br></div></code></pre></div></div>
<p>改代码 → Pi 里 <code>/reload</code> → 再测。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="82-查看扩展报错">8.2 查看扩展报错<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#82-%E6%9F%A5%E7%9C%8B%E6%89%A9%E5%B1%95%E6%8A%A5%E9%94%99" class="hash-link" aria-label="8.2 查看扩展报错的直接链接" title="8.2 查看扩展报错的直接链接" translate="no">​</a></h3>
<p>Pi 会报告插件错误并尽量继续运行。命令模式下：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pi </span><span class="token parameter variable" style="color:#36acaa">--mode</span><span class="token plain"> rpc</span><br></div></code></pre></div></div>
<p>会输出 <code>{"type":"extension_error", ...}</code> 事件。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="83-排查渲染问题">8.3 排查渲染问题<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#83-%E6%8E%92%E6%9F%A5%E6%B8%B2%E6%9F%93%E9%97%AE%E9%A2%98" class="hash-link" aria-label="8.3 排查渲染问题的直接链接" title="8.3 排查渲染问题的直接链接" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">PI_TUI_WRITE_LOG</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">/tmp/tui.log pi</span><br></div></code></pre></div></div>
<p>抓取原始 ANSI 输出流。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="84-类型检查">8.4 类型检查<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#84-%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5" class="hash-link" aria-label="8.4 类型检查的直接链接" title="8.4 类型检查的直接链接" translate="no">​</a></h3>
<p>在 Pi 包目录下做检查，让 <code>node_modules</code> 能正确解析：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token builtin class-name">cd</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">npm</span><span class="token string variable" style="color:#36acaa"> root </span><span class="token string variable parameter variable" style="color:#36acaa">-g</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">/@earendil-works/pi-coding-agent"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 把你的插件临时复制进来再检查</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx tsc </span><span class="token parameter variable" style="color:#36acaa">--noEmit</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--target</span><span class="token plain"> es2022 </span><span class="token parameter variable" style="color:#36acaa">--module</span><span class="token plain"> esnext </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--moduleResolution</span><span class="token plain"> bundler </span><span class="token parameter variable" style="color:#36acaa">--skipLibCheck</span><span class="token plain"> ./your-plugin.ts</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="85-确认命令是否注册成功">8.5 确认命令是否注册成功<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#85-%E7%A1%AE%E8%AE%A4%E5%91%BD%E4%BB%A4%E6%98%AF%E5%90%A6%E6%B3%A8%E5%86%8C%E6%88%90%E5%8A%9F" class="hash-link" aria-label="8.5 确认命令是否注册成功的直接链接" title="8.5 确认命令是否注册成功的直接链接" translate="no">​</a></h3>
<p>用 RPC 模式发一条 prompt：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'{"id":"1","type":"prompt","message":"/your-command"}'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> pi </span><span class="token parameter variable" style="color:#36acaa">--mode</span><span class="token plain"> rpc </span><span class="token parameter variable" style="color:#36acaa">--extension</span><span class="token plain"> ./p.ts</span><br></div></code></pre></div></div>
<p>看到 <code>"disposition":"handled"</code> 说明扩展命令被处理了。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-常见坑">9. 常见坑<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#9-%E5%B8%B8%E8%A7%81%E5%9D%91" class="hash-link" aria-label="9. 常见坑的直接链接" title="9. 常见坑的直接链接" translate="no">​</a></h2>
<table><thead><tr><th>现象</th><th>原因 / 解决</th></tr></thead><tbody><tr><td>插件不加载</td><td>路径不对；项目插件需要先 <code>/trust</code>；文件名必须是 <code>.ts</code>/<code>.js</code> 或目录含 <code>index.ts</code></td></tr><tr><td>改了代码没生效</td><td>执行 <code>/reload</code></td></tr><tr><td><code>ctx.ui.select</code> 返回 <code>undefined</code></td><td>处于 print/JSON 模式；先判断 <code>ctx.mode === "tui"</code></td></tr><tr><td>命令参数拿不到</td><td><code>args</code> 是原始字符串，需要自己 <code>trim()</code> / <code>split()</code></td></tr><tr><td>工具不被模型调用</td><td><code>description</code> 太模糊；说清楚"什么时候该用"</td></tr><tr><td>渲染错位 / 乱码</td><td>没按 <code>width</code> 截断；用 <code>truncateToWidth()</code></td></tr><tr><td>主题切换后颜色错乱</td><td>缓存了带 ANSI 转义的字符串；在 <code>invalidate()</code> 里重建</td></tr><tr><td><code>/reload</code> 后报错</td><td>在 <code>ctx.reload()</code> 之后还使用了旧运行时状态</td></tr><tr><td>资源泄漏</td><td>忘了在 <code>session_shutdown</code> 清理，或清理不幂等</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-进阶方向">10. 进阶方向<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#10-%E8%BF%9B%E9%98%B6%E6%96%B9%E5%90%91" class="hash-link" aria-label="10. 进阶方向的直接链接" title="10. 进阶方向的直接链接" translate="no">​</a></h2>
<table><thead><tr><th>目标</th><th>参考示例（Pi 包内 <code>examples/extensions/</code>）</th></tr></thead><tbody><tr><td>确认/拦截工具调用</td><td><code>permission-gate.ts</code></td></tr><tr><td>改提示词片段</td><td><code>prompt-customizer.ts</code></td></tr><tr><td>分支感知的工具状态</td><td><code>todo.ts</code></td></tr><tr><td>运行时动态注册工具</td><td><code>dynamic-tools.ts</code></td></tr><tr><td>命令里跑嵌套模型调用</td><td><code>qna.ts</code></td></tr><tr><td>替换主编辑器</td><td><code>modal-editor.ts</code></td></tr><tr><td>退出时做收尾</td><td><code>auto-commit-on-exit.ts</code></td></tr><tr><td>完整 TUI overlay 测试</td><td><code>overlay-qa-tests.ts</code></td></tr><tr><td>加模型供应商</td><td><code>custom-provider-gitlab-duo/</code></td></tr></tbody></table>
<p>示例目录：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;pi安装目录&gt;/examples/extensions/</span><br></div></code></pre></div></div>
<p>Windows：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">C:\Users\&lt;用户名&gt;\AppData\Roaming\npm\node_modules\@earendil-works\pi-coding-agent\examples\extensions\</span><br></div></code></pre></div></div>
<p><strong>分发插件</strong>：用 Pi 包（Pi packages），可以带上依赖、命令、技能、主题一起发布。见 <code>docs/packages.md</code>。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-速查表">11. 速查表<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#11-%E9%80%9F%E6%9F%A5%E8%A1%A8" class="hash-link" aria-label="11. 速查表的直接链接" title="11. 速查表的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="工厂函数">工厂函数<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E5%B7%A5%E5%8E%82%E5%87%BD%E6%95%B0" class="hash-link" aria-label="工厂函数的直接链接" title="工厂函数的直接链接" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ExtensionAPI </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-coding-agent"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">/* ... */</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="注册">注册<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E6%B3%A8%E5%86%8C" class="hash-link" aria-label="注册的直接链接" title="注册的直接链接" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> description</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handler</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> getArgumentCompletions</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerTool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">defineTool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> label</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> description</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> execute </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerShortcut</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"ctrl+shift+x"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> description</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handler </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerFlag</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-flag"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> description</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"boolean"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// 返回取消订阅函数</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="命令-handler">命令 handler<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E5%91%BD%E4%BB%A4-handler" class="hash-link" aria-label="命令 handler的直接链接" title="命令 handler的直接链接" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionCommandContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="工具-execute">工具 execute<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E5%B7%A5%E5%85%B7-execute" class="hash-link" aria-label="工具 execute的直接链接" title="工具 execute的直接链接" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">toolCallId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> signal</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> onUpdate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"..."</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> details</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="常用-ctx">常用 ctx<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E5%B8%B8%E7%94%A8-ctx" class="hash-link" aria-label="常用 ctx的直接链接" title="常用 ctx的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>属性 / 方法</th><th>说明</th></tr></thead><tbody><tr><td><code>ctx.mode</code></td><td><code>"tui"</code> / <code>"rpc"</code> / <code>"json"</code> / <code>"print"</code></td></tr><tr><td><code>ctx.hasUI</code></td><td>是否有可用 UI</td></tr><tr><td><code>ctx.cwd</code></td><td>工作目录</td></tr><tr><td><code>ctx.signal</code></td><td>当前回合的 AbortSignal</td></tr><tr><td><code>ctx.sessionManager</code></td><td>会话读写</td></tr><tr><td><code>ctx.ui.*</code></td><td>对话框、通知、状态</td></tr><tr><td><code>ctx.reload()</code></td><td>重载运行时（仅命令上下文）</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="官方文档">官方文档<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3" class="hash-link" aria-label="官方文档的直接链接" title="官方文档的直接链接" translate="no">​</a></h3>
<table><thead><tr><th>文档</th><th>内容</th></tr></thead><tbody><tr><td><code>docs/extensions.md</code></td><td>插件开发主文档</td></tr><tr><td><code>docs/tui.md</code></td><td>终端 UI 组件</td></tr><tr><td><code>docs/themes.md</code></td><td>主题机制</td></tr><tr><td><code>docs/settings.md</code></td><td>设置项</td></tr><tr><td><code>docs/packages.md</code></td><td>打包分发</td></tr><tr><td><code>docs/cli.md</code></td><td>命令行参数</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="附一个完整可跑的例子">附：一个完整可跑的例子<a href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/pi-extension-tutorial#%E9%99%84%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E5%8F%AF%E8%B7%91%E7%9A%84%E4%BE%8B%E5%AD%90" class="hash-link" aria-label="附：一个完整可跑的例子的直接链接" title="附：一个完整可跑的例子的直接链接" translate="no">​</a></h2>
<p>把下面内容存为 <code>~/.pi/agent/extensions/demo.ts</code>，重启 Pi（或 <code>/reload</code>）：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Type </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-ai"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> defineTool</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">ExtensionAPI</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@earendil-works/pi-coding-agent"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 1) 一个模型可调用的工具</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> wordCount </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">defineTool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"word_count"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  label</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Word Count"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"统计一段文本的单词数量"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  parameters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Type</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Object</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Type</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"要统计的文本"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> n </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">trim</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex char-set class-name" style="color:#36acaa">\s</span><span class="token regex regex-source language-regex quantifier number" style="color:#36acaa">+</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">filter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Boolean</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"text"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">单词数: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">n</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      details</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> n </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 2) 一个斜杠命令</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 3) 一个生命周期监听</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pi</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ExtensionAPI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerTool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">wordCount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">registerCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"demo"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"插件演示命令"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ui</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">notify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">demo 收到参数: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">args</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation function" style="color:#d73a49">trim</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation operator" style="color:#393A34">||</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation string" style="color:#e3116c">"(空)"</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"info"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"session_start"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 会话启动时的初始化，这里什么都不做</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>验证：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/demo hello</span><br></div></code></pre></div></div>
<p>以及让模型调用工具：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">用 word_count 工具统计 'hello world foo'，只报数字</span><br></div></code></pre></div></div>
<p>预期输出 <code>3</code>。</p>
<hr>
<p><em>教程结束。以你安装的 Pi 版本内 <code>docs/</code> 目录为准，API 可能有版本差异。</em></p>]]></content>
        <author>
            <name>Deepseek-AI</name>
            <uri>https://www.deepseek.com</uri>
        </author>
        <category label="技术" term="技术"/>
        <category label="Pi" term="Pi"/>
        <category label="插件开发" term="插件开发"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[博客开通]]></title>
        <id>https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/hello</id>
        <link href="https://wxhzhwxhzh.github.io/deepseek-helper-docs/blog/hello"/>
        <updated>2026-10-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[博客功能已开通，欢迎订阅。]]></summary>
        <content type="html"><![CDATA[<p>博客功能已开通，欢迎订阅。</p>
]]></content>
        <author>
            <name>SaoShen</name>
            <uri>https://github.com/wxhzhwxhzh</uri>
        </author>
        <category label="公告" term="公告"/>
    </entry>
</feed>