1. 为什么“批量删除历史对话”是个真需求豆包网页版用久了侧边栏的历史对话会像滚雪球一样越积越多。我自己的账号用了不到三个月侧边栏就攒了四百多条记录往下翻的时候浏览器明显卡顿找一条上周的对话得滚动半天。更麻烦的是有些对话涉及工作草稿、临时灵感、测试用的提示词留着占地方一条条删又太折磨人。豆包官方在网页版里给每条对话提供了删除入口但只支持单条操作。点开某条对话找到菜单点删除再确认一套流程下来少说五秒。四百条就是半个多小时纯体力活。所以“批量删除历史对话”这个需求本质上不是技术难题而是效率工具缺失带来的痛点。这篇文章面向三类人一是豆包网页版的重度用户历史记录已经多到影响使用二是对浏览器脚本、前端调试感兴趣想拿这个场景练手的开发者三是单纯想搞清楚“网页版到底能不能批量删、怎么删才安全”的普通用户。我会把几种可行方案从原理到实操讲透包括我自己踩过的坑和最终稳定跑通的流程。需要先说明一点豆包网页版的产品形态会更新界面元素和接口路径可能变化。我下面讲的方法论和排查思路是通用的具体的选择器、请求参数需要你根据当时的页面实际情况做调整。这也是为什么我不建议你直接抄一段脚本就跑而是理解原理后自己适配。2. 批量删除的三条技术路线与选型逻辑2.1 路线一模拟点击最直观但最脆弱最容易被想到的方案是用浏览器自动化工具比如在控制台里写一段脚本模拟用户点击每一条对话的删除按钮。思路很直接找到侧边栏所有对话条目遍历它们对每一条执行“打开菜单→点击删除→确认”的动作序列。这条路线的好处是不依赖任何接口知识你看到什么就点什么逻辑透明。但问题也很明显豆包网页版的侧边栏是虚拟滚动或者懒加载的一屏只渲染可见的十几条你脚本跑的时候没渲染出来的条目根本不在 DOM 里。你得先模拟滚动等新条目加载出来再继续节奏很难控制。而且每次删除后列表会重排索引会错位稍不注意就点错或者漏点。我最早试的就是这条路写了大概三十行脚本跑起来删了七八条就开始乱要么重复点同一条要么跳过。调试成本很高不适合非开发者。2.2 路线二直接调用删除接口快且可控真正高效的方案是绕过 UI直接找到豆包网页版删除对话时调用的后端接口然后用脚本批量发请求。原理是你在页面上手动删一条对话浏览器开发者工具的 Network 面板里会记录下这个请求包括请求地址、方法、请求头、请求体。把这个请求的结构搞清楚就可以用代码循环调用。这条路线的好处是速度快、可控性强。一条请求几十毫秒四百条对话理论上十几秒就能处理完。而且你可以先拉取对话列表拿到所有对话 ID再按 ID 逐个删除不依赖页面渲染。难点在于两点一是要正确携带身份凭证通常是 Cookie 或者请求头里的某个 token这个从你已登录的浏览器里直接取就行二是要搞清楚列表接口和删除接口的参数格式不同版本可能不一样。2.3 路线三半自动辅助折中但稳如果你不想碰接口又觉得纯手动太慢还有一条折中路写一个辅助脚本帮你自动展开菜单、自动点击删除、自动确认但滚动和加载由你手动控制。你滚一屏脚本删一屏删完你再滚下一屏。这条路线的好处是风险低因为操作节奏由人把控不会因为页面没加载完就乱点。缺点是还是需要你参与没法完全挂机。但对于历史记录不是特别多、或者对脚本不太放心的用户这是最稳妥的选择。下面这张表把三条路线做个对比方便你按自己的情况选路线技术门槛速度稳定性适合人群模拟点击中慢低想练手的前端新手调用接口中高快高有开发经验、追求效率半自动辅助低中高普通用户、求稳我最终采用的是路线二下面重点讲这条。路线三我会在实操部分给一个简化版脚本供不想碰接口的读者使用。3. 动手前的准备工作与关键参数获取3.1 环境与工具清单你不需要装任何额外软件一台能正常登录豆包网页版的电脑、一个现代浏览器Chrome 或 Edge 都行就够了。核心工具是浏览器自带的开发者工具按 F12 或者右键“检查”就能打开。我建议你专门开一个浏览器窗口来做这件事不要和其他重要标签混在一起避免误操作。另外操作前先把豆包网页版刷新一次确保页面状态干净。3.2 抓取删除请求的完整过程这是整个方案最关键的一步。打开开发者工具切到 Network 面板勾选“Preserve log”保留日志过滤条件先不设或者设成 Fetch/XHR这样只看接口请求。然后回到页面手动删除一条不重要的历史对话。删完之后Network 面板里会多出几条请求。你要找的是那条方法为 DELETE 或者 POST、路径里带有 conversation、chat、message 之类关键词的请求。点开它重点看四个东西Request URL完整的接口地址注意里面的对话 ID 部分那就是你要替换的变量。Request Method通常是 DELETE 或 POST。Request Headers重点看 Cookie、Authorization、以及可能的 x-* 自定义头。这些是身份凭证缺一不可。Request Payload如果是 POST看看请求体里带了什么参数比如对话 ID、时间戳等。把这条请求右键选择“Copy as cURL”粘贴到文本编辑器里你就能看到完整的请求结构。这一步做完你手里就有了删除单条对话的“钥匙”。3.3 找到对话列表接口光有删除接口还不够你需要知道要删哪些对话的 ID。所以还得找到拉取历史对话列表的接口。同样在 Network 面板里刷新页面或者滚动侧边栏观察新出现的请求。列表接口通常返回一个 JSON里面包含对话的 ID、标题、创建时间等字段。找到之后同样用“Copy as cURL”把请求结构保存下来。这个接口一般支持分页参数比如 offset、limit、cursor 之类。你需要搞清楚它的分页方式才能把所有对话都拉全。注意抓包过程中不要泄露你的 Cookie 和 token 给任何人。这些凭证等同于你的登录态别人拿到就能操作你的账号。文章里我只会用占位符演示你自己操作时也要注意截图打码。3.4 参数计算分页拉取全部对话 ID假设列表接口每页返回 20 条你的账号有 400 条对话那就需要拉 20 页。分页参数常见的有两种一种是 offset/limitoffset 从 0 开始每次加 20另一种是 cursor上一页返回一个 next_cursor下一页带上它。你需要先手动请求第一页看看返回结构里有没有 total 字段或者 has_more 字段。如果有 total直接算出总页数如果有 has_more就循环请求直到它为 false。这一步的逻辑不复杂但必须做对否则会漏删或者重复删。我自己的账号当时是 437 条列表接口每页 30 条has_more 为 true 时继续。我写了个循环把每页的对话 ID 收集到一个数组里最后得到完整的 437 个 ID。这个过程大概花了五六秒。4. 完整实操流程与核心代码实现4.1 第一步在控制台拉取全部对话 ID打开豆包网页版登录后按 F12 打开控制台Console 面板。把下面这段代码粘贴进去注意把LIST_API_URL替换成你抓到的列表接口地址把 headers 里的凭证替换成你自己的。// 拉取全部对话 ID 的示例逻辑 const LIST_API_URL https://你的豆包域名/api/conversation/list; const headers { Cookie: 你的完整Cookie, Authorization: 你的token, Content-Type: application/json }; async function fetchAllConversationIds() { let allIds []; let hasMore true; let offset 0; const limit 30; while (hasMore) { const resp await fetch(${LIST_API_URL}?offset${offset}limit${limit}, { method: GET, headers: headers, credentials: include }); const data await resp.json(); const items data.data?.list || data.list || []; items.forEach(item allIds.push(item.id || item.conversation_id)); hasMore data.data?.has_more ?? data.has_more ?? false; offset limit; console.log(已拉取 ${allIds.length} 条); await new Promise(r setTimeout(r, 300)); // 控制频率 } console.log(全部 ID, allIds); return allIds; } fetchAllConversationIds();这段代码的核心是循环请求列表接口把每页的对话 ID 收集起来。await new Promise(r setTimeout(r, 300))这行是故意加的延迟避免请求太密集触发风控。我实测下来300 毫秒的间隔比较稳太快了偶尔会返回 429。跑完之后控制台会打印出所有对话 ID。你可以把它复制出来存一份万一后面删错了还能对照。4.2 第二步批量发送删除请求拿到 ID 数组后接着写删除逻辑。同样在控制台里执行// 批量删除的示例逻辑 const DELETE_API_URL https://你的豆包域名/api/conversation/delete; const deleteHeaders { Cookie: 你的完整Cookie, Authorization: 你的token, Content-Type: application/json }; async function batchDelete(ids) { let success 0; let failed []; for (let i 0; i ids.length; i) { const id ids[i]; try { const resp await fetch(DELETE_API_URL, { method: POST, headers: deleteHeaders, credentials: include, body: JSON.stringify({ conversation_id: id }) }); if (resp.ok) { success; } else { failed.push({ id, status: resp.status }); } } catch (e) { failed.push({ id, error: e.message }); } if (i % 10 0) { console.log(进度${i 1}/${ids.length}成功 ${success}); } await new Promise(r setTimeout(r, 200)); } console.log(完成成功 ${success} 条失败 ${failed.length} 条); console.log(失败列表, failed); } // 把上一步拿到的 ID 数组传进来 batchDelete(allIds);这里有几个细节值得说。第一删除请求的方法和请求体格式必须和你抓包看到的一致有的版本是 DELETE 方法加 query 参数有的是 POST 加 JSON body不能想当然。第二延迟我设的是 200 毫秒比拉取时更保守因为删除是写操作风控更敏感。第三失败列表一定要打印出来方便你事后补删。我自己的 437 条对话用这个脚本跑了大概两分半钟成功 431 条失败 6 条。失败的几条我单独重跑了一次就过了估计是偶发的网络抖动。4.3 第三步验证删除结果脚本跑完后不要急着关页面。先刷新豆包网页版看侧边栏的历史对话是不是清空了。如果还有残留可能是列表接口有缓存或者有些对话不在你拉取的范围内比如归档的、置顶的。我遇到过一个情况有十几条对话是“置顶”状态列表接口默认不返回它们所以脚本没删到。后来我手动取消置顶再跑一次就解决了。所以你在拉取 ID 之前最好先确认一下有没有置顶对话有的话先取消置顶。另外删除操作通常是软删除服务端标记一下前端不再展示。如果你发现删完刷新又出现了可能是接口调用没真正生效检查一下返回状态码是不是 200。4.4 半自动辅助脚本不想碰接口的看这里如果你不想折腾接口这里给一个半自动方案。它的逻辑是你手动滚动侧边栏脚本自动帮你点删除。你只需要在控制台粘贴一次然后滚动页面就行。// 半自动辅助自动点击删除按钮 setInterval(() { // 找到所有还没删除的对话条目的菜单按钮 const menuButtons document.querySelectorAll([data-testidconversation-more]); menuButtons.forEach(btn { if (btn.dataset.processed) return; btn.dataset.processed 1; btn.click(); setTimeout(() { const deleteBtn document.querySelector([data-testiddelete-conversation]); if (deleteBtn) deleteBtn.click(); setTimeout(() { const confirmBtn document.querySelector([data-testidconfirm-delete]); if (confirmBtn) confirmBtn.click(); }, 200); }, 200); }); }, 1000);这段代码里的选择器>