资讯动态

用Playwright批量删除QQ空间说说:自动化脚本的完整实战与避坑指南

发布时间:2026/10/14 9:48:03 来源:尧图企业网站定制
简介专门用于批量清理QQ空间说说的浏览器插件面向动态较多、需要快速整理历史内容的QQ用户兼容2023年新版QQ空间界面可登录网页版后按筛选条件自动删除并支持ESC键随时终止操作。资源包共3个文件、大小仅2.74MB核心为1个JavaScript脚本另附2个RAR压缩包分别提供Tampermonkey扩展安装程序与附加清理工具方便用户搭建脚本运行环境并扩展功能。目前已有1128人下载学习适合普通用户与轻度技术爱好者快速上手。通过该脚本可大幅减少逐条删除说说的重复劳动尤其适合动态数量庞大、历史内容繁杂的用户同时附带详细配置说明让不熟悉浏览器扩展安装路径的用户也能按图索骥。资源整合了操作脚本、运行环境与清理工具形成一套相对完整的QQ空间说说清理方案。1. 从“删了一下午还剩两百条”说起这款说说删除插件到底省了什么想批量删除QQ空间说说的时候大多数人以为难点在“点删除”这个动作上真正绑过手的人才知道瓶颈是两个更磨人的问题一是怎么把几年前的历史说说全部加载出来二是不同类型说说对应的删除入口还不一样。某开发者的空间攒了上千条说说手动删了一下午还有两百条。这份资源名义上叫插件实际本体是一套驱动浏览器的自动化脚本自动下拉加载、自动找到删除按钮、自动处理确认弹窗直到列表被清到预设条数。和手点相比它把时间从几小时压缩到几十分钟而且每一步都有日志可查。它适合两类人空间里积攒了几百上千条说说、想全部清空或只留最近几条的用户以及玩浏览器自动化、想研究动态页面加载逻辑的开发者。下文按选型、实现、参数和坑位完整拆一遍。2. 为什么选择浏览器自动化说说批量删除的三种方案对比2.1 手工逐条删除慢、重复还有操作频率限制QQ空间网页版里要删除自己的说说需要把鼠标移到说说右上角等操作条浮出来点删除图标再在弹窗里点一次确认。这个交互设计是为了防误删对手工删除非常不友好单条操作至少三次点击每次还要等页面反应。几百条说说就意味着上千次重复点击手指酸不算什么误删才麻烦。比手滑更头疼的是平台对删除动作有频率限制。短时间连续删除会弹“操作过于频繁请稍后再试”。这个限制不公开具体次数不同账号触发阈值也不一样。有人说每分钟十几次有人说可以到二十次但都不能作为设计依据。真实体验是删到几十条时网络请求开始变慢随后就出现验证要求。手工方案还有一个隐性成本你需要从最新一条开始一路翻到很多年前。说说列表是动态加载的老内容埋得很深一次性把全部历史翻完本身就要滚动很多次而且每次滚动都会触发新的网络请求。删除这个动作只占工作量的一半另一半全花在“把历史内容找出来”上。所以手工删除在小批量场景比如就删三五条不留的内容完全够用一旦超过 50 条效率和体验都断崖式下降。想批量删除QQ空间说说必须换工具。2.2 控制台注入脚本免安装但调试成本高很多在网上搜过代码片段的人会想到在浏览器控制台里粘贴一段 JavaScript把页面上所有“删除”按钮依次点掉。这个思路本身没毛病但落地时会遇到几道坎。第一个问题是说说列表的懒加载。控制台脚本只能操作已经渲染出来的 DOM 节点当你把脚本一次性粘贴执行完它只处理了当时页面里已加载的内容。而当页面继续往下滚动时新加载的说说里也有删除按钮但脚本早就跑完了。于是你得自己写滚动定时器自己处理加载完成事件这已经超出了大多数粘贴党的水平。第二个问题是删除确认弹层。空间页面的删除需要二次确认如果一个脚本只点了删除却没人点确认操作会卡在弹窗里。而且弹窗里的确认按钮文案在不同版本下可能是“确定”“确定删除”或者“删除”你得写额外的选择器去兼容。第三个问题是无法观察和恢复。控制台脚本一旦中途报错整段上下文就被销毁你刷新页面所有状态清零删到一半的进度要靠自己记住。真出了误删、选错条目的问题也没有截图回放可查。所以控制台注入适合懂一点前端的人临时调试不适合作为批量删除几百条的常规方案。2.3 浏览器自动化可控、可停、可查日志最终选的是浏览器自动化框架这里用的是 Playwright。它的工作方式相当于在你面前打开一个真实的浏览器由代码去控制滚动、移动鼠标、点击按钮操作过程和真人手动几乎一致。它不依赖抓取内部接口也不需要逆向消息鉴权QQ空间的前端代码自己会把相关参数生成好我们只是把鼠标应该做的事重放给它。选 Playwright 而不是 Selenium主要是两点一是 Playwright 的默认等待逻辑更严格点击前会等元素可见、可交互不容易出现“元素没加载就去点”的时序问题二是它对登录态的保存很友好可以把扫码登录后的 cookie 和本地存储整个存成一个文件下次运行直接复用不用每次扫码。运行环境非常简单一台能联网的电脑装 Python 3.10 或更高版本然后执行两行命令pip install playwright playwright install chromium第一行安装 Playwright 的 Python 包第二行把它配套的 Chromium 浏览器下载到本机用户目录下。这个 Chromium 是独立运行的自动化内核不会影响日常使用的浏览器。装好之后可以先用一个 10 行脚本确认环境可用from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://h5.qzone.qq.com/) page.wait_for_timeout(3000) browser.close()这段代码启动了自动化浏览器并打开了空间首页看到页面正常加载就说明驱动没问题。之后的删除逻辑都是在这个基础上叠加的。设计思路上整套批量删除分成三个阶段登录态获取、滚动加载、删除确认。每个阶段可以独立测试也可以单独改参数下一章具体拆这三步。3. 批量删除说说的落地流程登录态、滚动加载、删除确认三步走3.1 第一步保存并复用登录态批量删除是需要高频操作的如果每次运行都扫码登录脚本的可用性会大打折扣更重要的是频繁登录本身也可能触发安全验证。常见做法是第一次运行程序时让浏览器打开登录页你手动扫一次码登录成功后程序把整个浏览器的会话数据存成文件以后再启动读取这个文件直接就是已登录状态。这段代码负责“保存登录态”from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) context browser.new_context() page context.new_page() page.goto(https://h5.qzone.qq.com/) page.pause() # 脚本在这里暂停你手动完成扫码登录 context.storage_state(pathqzone_state.json) # 保存会话 browser.close()代码解释page.pause() 是 Playwright 提供给人介入的暂停点程序运行到这里会停住你可以用真实扫码或手机登录。登录成功并进入空间主页后在终端里按一下 Enter脚本继续执行随后把会话保存到 qzone_state.json。再次运行时就不需要扫码了直接加载这个文件context browser.new_context(storage_stateqzone_state.json)有一点要提醒登录态文件相当于账号凭证存放位置要放在自己能控制、别人拿不到的目录里。如果你的电脑多人共用跑完脚本后记得随手删掉这个文件。3.2 第二步滚动加载全部说说说说列表是典型的分段加载页面。页面初打开只渲染最近的一二十条你滚动到底部它才请求下一段。要删干净就得先把整个历史列表全部“逼”出来。最简单的方法是滚底判断法反复把滚动条拉到底部等两秒统计页面里已经出现多少条说说如果连续两轮数量没变说明已经到底停止滚动。def scroll_all_feeds(page, max_scroll80): last_count 0 for i in range(max_scroll): page.evaluate(window.scrollTo(0, document.body.scrollHeight)) page.wait_for_timeout(2000) count page.evaluate(document.querySelectorAll(.feed-item).length) print(f第 {i1} 次滚动共加载 {count} 条) if count last_count: break last_count count return last_count这里有两个参数值得展开。max_scroll 是滚动上限默认 80 次防止列表加载逻辑异常时脚本无限循环如果你确认自己的说说非常多可以把这个值调大。每次滚动后固定等 2000 毫秒是因为列表请求需要时间等太久会拖慢整体效率等太短可能数量还没更新。2000 毫秒是在大部分网速下试出来的折中值。滚动容器是另一个坑后面避坑章节会详细讲。简单说如果页面里的滚动容器不是整个文档而是某个嵌套的 div那么 window.scrollTo 不生效你要把滚动目标换成那个具体元素。一个通用做法是先从页面里找出 overflow 属性为 auto 或 scroll 的容器再对它做滚动。但第一版脚本建议先用 window.scrollTo 跑一遍跑不动再换因为两种页面的选择器差异往往很大。3.3 第三步定位删除按钮并处理二次确认当所有说说都加载出来后开始正式的删除循环。每个说说的操作可以拆成四步悬停到条目上、等待操作条出现、点击删除、在确认弹窗里点确定。悬停这一步经常被忽略。新版空间说说右上角的操作入口默认是隐藏的鼠标悬浮到说说区域内才会显示。自动化里对应的就是 hover 动作def delete_one_feed(page, feed): feed.scroll_into_view_if_needed() # 先把条目滚到可视区 feed.hover() # 悬停让操作条渲染出来 page.wait_for_timeout(400) delete_btn feed.query_selector(text删除) if not delete_btn: print(当前条目没有找到删除入口跳过) return False delete_btn.click() confirm page.wait_for_selector(text确定, timeout5000) confirm.click() page.wait_for_timeout(1500) return True为什么删除按钮要在 feed 内部查找而不是整个页面查找因为说说正文里也可能出现“删除”两个字比如一条说说写“删除烦恼重新出发”全页面找第一个 text删除 就会点错对象。把范围限定在 feed 内部后误匹配概率会小很多。确认弹窗用“确定”来定位。QQ空间历史上用过“确定”“确定删除”“删除”几种文案第一版脚本先写最常见的一种遇到定位失败就进入兜底逻辑。等待时间给到 5 秒原因是弹窗是异步渲染的网络稍慢时可能延迟出现给太短容易误判失败。删完一条后不要立刻进行下一条等 1500 毫秒左右让页面把 DOM 更新完。如果不等后面的条目标记会有脏数据越删越不准。3.4 一个可运行的删除主循环把上面三个部分串起来就是一个能用的最小版本from playwright.sync_api import sync_playwright def run_cleanup(): with sync_playwright() as p: browser p.chromium.launch(headlessFalse) context browser.new_context(storage_stateqzone_state.json) page context.new_page() page.goto(https://h5.qzone.qq.com/) page.wait_for_timeout(3000) total scroll_all_feeds(page) print(总共有, total, 条说说) feeds page.query_selector_all(.feed-item) deleted 0 for feed in feeds: ok delete_one_feed(page, feed) if ok: deleted 1 print(已删除, deleted, 条还剩, total - deleted) if deleted % 20 0: page.reload() page.wait_for_timeout(3000) # 刷新后需要重新滚动加载这里省略实际工程需补上 browser.close() if __name__ __main__: run_cleanup()这个主循环故意做得很简单把刷新后重新加载的逻辑省略是为了让批量删除的主线清晰。真实使用中如果刷新了页面原来拿到的 feeds 列表会全部失效所以稳妥的做法是不在循环里刷新而是整批跑完后手动重新运行一次。下一章的避坑记录里也会提到这个取舍。代码里用到的 .feed-item 选择器是编写时对新版页面结构的命名并不保证在你的账号页面上一成不变。第 5 章会专门讲怎么用开发者工具确认当前页面到底该用哪组选择器。4. 实操避坑与高频排查五条翻车记录4.1 滚动到底却不再加载新说说现象脚本执行了很多次 window.scrollTo(0, document.body.scrollHeight)但页面上说说的数量一直停在某个数后面几年里的旧说说看不到。原因QQ空间网页版经过多次改版滚动容器不一定是最外层文档。有些版本的说说列表在一个独立的 div 内部滚动比如列表容器自身设置了 overflow-y: auto这时候整个页面的高度并不增长你把 window 拉到最底部列表容器实际没动。解决打开开发者工具点击定位到说说列表的父容器看它的样式里是否有 overflow 属性。如果有就把滚动目标从 window 换成那个容器container page.query_selector(.feed-list-wrapper) page.evaluate((el) el.scrollTop el.scrollHeight, container)如果页面有多层嵌套滚动最省事的做法是直接给容器 scrollTop 赋一个很大的值然后等加载、再赋一次我调试这类页面时习惯用这种方式兜底。4.2 删除按钮找不到或点击无反应现象hover 操作已经做了也加了 400 毫秒等待但 feed 内部就是查不到“删除”文本偶尔查到了点下去页面没有任何反应。原因删除入口有两种。老版本是悬浮后直接出现“删除”文字链接新版本把“删除”收进了“…”更多按钮的菜单里悬浮后要先点开“…”才能看到删除。另外如果你的鼠标悬停目标不是说说的外层容器而是某个子元素部分版本的浮层渲染条件也会失灵。解决在删除入口查找失败时自动尝试点击条目内部的“…”按钮more_btn feed.query_selector(text更多) if more_btn: more_btn.click() page.wait_for_timeout(500) delete_btn page.query_selector(.menu-item:has-text(删除))这里的菜单项选择器也要按实际页面调整。建议编写时开着开发者工具观察一下悬停之后浮层实际出现在哪个 DOM 层级里再决定从 feed 里找还是从 body 里找。4.3 确认弹窗文案变化定位失败现象点击删除后确实弹了确认框但脚本在 5 秒内没等到 text确定于是抛超时异常人工看时发现弹窗上写的是“确定删除”。原因QQ空间不同版本、不同账号类型下弹窗按钮文案有差异。“确定”和“确定删除”是出现频率最高的两种。用完全匹配的 text 定位方式遇到文案一变就失效。解决放弃精确匹配用“包含”语义的选择器或者把多个候选文案轮询一遍for keyword in [确定删除, 确定, 删除]: btn page.query_selector(ftext{keyword}) if btn: btn.click() break选项顺序上把“确定删除”放在“确定”前面避免先匹配到半截文案。实际执行时只要两个能命中一个删除语义都是一样的。4.4 跑十几分钟后要求安全验证现象脚本刚开跑很顺畅删了二十多条以后突然页面弹出滑块验证码脚本卡住后续所有删除都无法继续。原因删除自己的内容会改变账号历史数据属于高风险操作。平台对删除频率有隐蔽限制不同账号阈值不同。连续快速删除很容易触发滑块验证或者短信验证。解决把所有固定延时改成随机延时并增加阶段性暂停。比如每次删除后随机等待 1500 到 4000 毫秒每删 20 条强制休息 30 到 60 秒。下面这段是随机延时的写法import random page.wait_for_timeout(random.randint(1500, 4000))如果验证码还是出现就停在原地等人工处理处理完后再继续。千万别把延时压得太低我按这个思路跑清理任务时把单条平均耗时控制在 5 秒左右整批下来没有触发过安全验证。4.5 删完的说说又出现或者计数不减现象点击确认后脚本认为删除成功了但下一次加载或刷新页面那条说说还在。日志里显示删除成功实际总数没有变化。原因这一类通常是删早了。删除弹窗点击后前端要发请求等接口返回接口失败或超时时前端会在页面里保留那条数据。而脚本只盯着确认按钮点完就当完成没有验证结果。解决点击确认后加一道“等待条消失”的校验。先拿条目标识比如>before_count page.evaluate(document.querySelectorAll(.feed-item).length) # ...点击确认... page.wait_for_timeout(1500) after_count page.evaluate(document.querySelectorAll(.feed-item).length) if after_count before_count: print(删除失败可能接口未返回)计数不减少还有一个隐藏原因页面里加载的说说有部分是“置顶”或“仅自己可见”的分组滚动列表里已经删完但顶部分组里还有一条。这时候要把页面顶上的“全部说说”筛选切换一次再跑一遍。5. 参数设置与边界随机延时、分批执行和页面适配5.1 时间参数随机延时的区间怎么定先给一张参数表参数推荐值作用hover 后等待400ms等待操作条渲染点击删除后等待800-1200ms等待确认弹窗出现点击确认后等待1200-2000ms等待接口返回与 DOM 更新删除间隔1500-4000ms 随机避免固定节奏触发安全验证每 20 条暂停30-60s降低频率保护账号滚动加载等待1500-2500ms等待列表新内容请求完成表里的删除间隔是这个脚本最关键的参数。调太慢删一千条要一个多小时调太快账号风险骤增。建议第一版先按 1500 到 4000 毫秒跑 50 条试水没有验证码再尝试把下限缩到 1000 毫秒。有一点要说明随机延时不能完全避免风控它只是让操作节奏更像真人。如果账号本身被重点盯防再优秀的节奏也没用这种场景直接放弃自动化更合适。5.2 分批执行一次一百条别贪多超过一百条的清理任务不建议在一个浏览器会话里一口气跑完。原因有两个一是浏览器长时间运行会出现内存膨胀列表越长页面 DOM 越多卡顿概率越高二是中途断点恢复成本高一旦被验证码打断之前加载的列表没了又要从头滚动。我一般会把任务拆成批次每批 100 条以内。每批跑完手动或自动重启浏览器一次重新加载登录态继续下一批。配合日志文件记录已完成的数量即使中断也知道从哪继续done_file cleanup_done.txt with open(done_file) as f: done int(f.read() or 0) # 每删除一条成功后更新 done open(done_file, w).write(str(done))进度文件的好处是把“删到哪了”从内存里挪到磁盘上刷新页面也不丢。5.3 选择器适配与边界代码里写的 .feed-item、text删除、text确定 都只是针对编写时页面结构的快照。QQ空间前端会灰度发布同一时间不同账号看到的页面结构可能不同甚至同一账号隔几天再跑一次就会变化。遇到选择器失效时按固定流程处理先打开开发者工具在元素面板中选中一条说说看它的 class 名称和包裹菜单的层结构然后在代码里的选择器列表先加一条新的再在删除入口找不到时启用“…”菜单兜底每次跑完都留一份脱敏的页面快照方便后面翻阅。选择器适配这件事没有一次性解决方案只能在每次改版后修一次。好在这套逻辑的修改点集中在前面的选择器常量和删除入口函数里其余代码不用大动。5.4 不适合用脚本的场景如果你的账号每次删除都弹验证码说明安全级别很高继续自动化只会增加账号被限制的风险如果你需要清理的是他人的说说、评论或转发内容这个脚本不适用如果你只删三五条手工点反而更快。边界清晰之后这个脚本才是一个趁手的工具而不是一个到处硬套的万能方案。6. 进阶技巧删除前的备份、保留最近 N 条、验证删干净6.1 先把备份做扎实删除是不可逆操作建议在跑批量删除之前先把所有可见说说的正文和链接抓下来存到本地。备份代码不复杂在滚动加载完成后、删除循环开始前插入import json feeds page.query_selector_all(.feed-item) backup [] for feed in feeds: text feed.inner_text() backup.append(text) with open(backup.json, w, encodingutf-8) as f: json.dump(backup, f, ensure_asciiFalse, indent2)备份之后别急着删至少等到文件大小合理后再进入删除流程。见过有人在备份文件只有几千字节时就开删最后找不到历史说说才后悔。6.2 保留最近 N 条不是每个人都要删成零条。很多人只是想把空间清清爽爽留下最近几条。做法也很简单在删除循环里加一个剩余量判断keep_count 30 total len(feeds) for i, feed in enumerate(feeds): if total - i keep_count: print(已保留最近, keep_count, 条停止) break delete_one_feed(page, feed)注意这里的前提是列表顺序为“新的在上、旧的在下”保留前 30 条即可。如果你的空间支持置顶这个逻辑要考虑置顶条目的干扰。6.3 删完之后的验证跑完之后刷新页面进入说说列表看看总数是不是预期值。批量删除最怕“看起来删了实际还有残留”。我会做一个简单校验从最后一条开始往上数确认剩余条数等于 keep_count 或 0如果数量偏多多半是有部分说说藏在“分组”或“私密”页签里需要切换筛选后重新执行一遍滚动和删除。从那以后我每次清理空间任务都会先备份、设好保守的延时区间、跑一个批次先验证计数再去处理大几千条的清空需求。这个流程多花半小时但省掉的返工时间远不止半小时。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑