基于UI自动化与Playwright的微信好友状态检测技术实战

📅 2026/7/29 10:31:50
基于UI自动化与Playwright的微信好友状态检测技术实战
1. 项目概述与核心价值最近在技术社区和朋友圈里时不时会看到有人讨论“如何悄无声息地检测微信里谁删了你”。这背后反映的其实是一个普遍存在但又略显尴尬的社交需求微信好友列表越拉越长但其中有多少人已经单方面将你删除你却毫不知情。手动检测一个个发消息或者拉群太麻烦还容易打扰到别人。于是各种“一键检测”工具应运而生它们号称能快速、无感地帮你找出那些“僵尸好友”。作为一个在自动化工具和客户端逆向领域摸爬滚打多年的开发者我决定深入探究一下这类工具的实现原理并亲手打造一个。这不仅仅是为了解决一个具体问题更是一次绝佳的实战机会能让我们深入理解现代即时通讯软件的协议、客户端安全机制以及自动化测试技术。今天我就把这次从零到一的完整实战过程包括技术选型、核心原理、代码实现、避坑经验毫无保留地分享出来。无论你是想自己动手实现类似功能还是单纯对背后的技术感兴趣这篇文章都能给你带来实实在在的收获。2. 技术方案选型与核心思路拆解在动手之前我们必须明确一个核心原则任何操作都必须在符合相关服务条款和法律法规的前提下进行。我们的目标是技术研究与学习探讨在合法合规框架下实现自动化检测的可能性与边界。2.1 主流实现路径分析市面上和社区里流传的检测方法五花八门但归根结底技术路径主要可以归纳为以下三类协议模拟与逆向分析这是最“硬核”的方法。通过抓包、逆向工程分析微信客户端与服务器之间的通信协议然后模拟客户端行为发送特定的探测消息。这种方法技术门槛最高需要对网络协议、加密解密、客户端结构有很深的理解且随着客户端更新协议可能变动维护成本巨大。更重要的是深度逆向和模拟协议可能触及法律红线是我们必须坚决规避的。官方接口的创造性使用微信为开发者提供了丰富的开放接口如微信公众平台、小程序、企业微信等。我们可以思考能否利用这些合法接口间接实现检测例如通过服务号模板消息、小程序客服消息等渠道向用户发送一条特殊的、对用户无打扰的提示信息通过发送成功与否的状态来判断。这种方法的优点是完全合规缺点是依赖用户授权如关注服务号、使用小程序且接口有调用频率限制无法做到完全“无感”和批量快速。UI自动化测试技术这是目前个人开发者实现此类工具最可行、相对安全的技术路径。其核心思路是不触碰底层协议而是在手机或电脑上自动操作微信客户端界面模拟人的点击、输入等行为通过观察界面反馈如是否出现红色感叹号、转账时的提示信息来判断好友状态。这种方法本质上是在操作一个“黑盒”不涉及协议破解风险较低。综合考量技术可行性、合规风险和学习价值我们将选择UI自动化测试作为本次实战的核心技术方案。具体来说就是在电脑上通过自动化脚本控制微信PC版进行操作。2.2 工具链选型与理由确定了UI自动化的方向后我们需要选择合适的工具。自动化框架Playwright或Selenium。两者都是优秀的浏览器自动化工具。考虑到微信PC版本质上是一个基于CEFChromium Embedded Framework的桌面应用其界面与Web页面高度相似这些工具同样能对其中的Web视图进行控制。Playwright相对较新在API设计、执行速度和稳定性上更有优势我们选择Playwright。编程语言Python。在自动化、脚本编写和快速原型开发方面Python拥有无与伦比的生态优势和简洁语法社区资源丰富适合本项目。控制对象微信PC版窗口。我们将自动化操作的对象锁定为微信PC版的窗口界面而非手机。这样做的原因有三一是PC屏幕大元素定位更稳定二是可以在同一台机器上完成开发和测试三是避免了手机设备管理和投屏的复杂度。重要提示本实战指南的所有操作均基于技术学习与研究的目的是展开。在实际自动化操作任何软件时请务必遵守其用户协议控制操作频率避免对服务器造成不必要的压力严禁用于任何恶意或骚扰用途。3. 核心原理与实操环境搭建3.1 检测原理深度剖析我们的自动化脚本将模拟以下人工操作流程并解析其反馈发起转账这是目前公认干扰最小、最可靠的检测方法之一。向目标好友发起转账不输入密码不真正转出。解析反馈在输入转账金额的界面微信会进行实时校验。如果对方仍是你的好友你会正常进入输入密码界面或提示你输入密码。如果对方已将你删除在点击转账按钮后微信通常会直接弹出一个提示框内容类似于“你不是收款方的好友……”。逻辑判断自动化脚本的核心就是捕捉这个提示框的出现。如果出现则判定为“被删除”如果顺利进入下一步则判定为“正常好友”。这个方法之所以有效是因为“转账”功能涉及真实的资金往来微信必须在此刻进行严格的好友关系校验这个校验结果是实时且准确的。3.2 开发环境准备接下来我们一步步搭建开发环境。步骤一安装Python与Playwright确保你的电脑上安装了Python3.7及以上版本。然后通过pip安装Playwright。# 安装playwright库 pip install playwright # 安装Playwright所需的浏览器驱动主要是Chromium微信PC版基于此 playwright install chromium步骤二启动微信PC版并登录手动启动微信PC版并扫码登录你的账号。确保微信窗口处于打开状态。步骤三定位微信窗口这是关键一步。我们需要获取微信PC版窗口的调试端口以便Playwright能够连接并控制它。微信PC版默认不开放调试端口我们需要通过启动参数来开启。完全退出微信。找到微信的快捷方式或可执行文件在其“属性”的“目标”一栏中末尾添加以下启动参数注意前面有个空格--remote-debugging-port9222例如原本是C:\Program Files\Tencent\WeChat\WeChat.exe修改后为C:\Program Files\Tencent\WeChat\WeChat.exe --remote-debugging-port9222使用修改后的快捷方式启动微信。此时微信会开放一个本地调试服务地址通常是http://127.0.0.1:9222。我们可以写一个简单的Python脚本来测试连接import asyncio from playwright.async_api import async_playwright async def main(): async with async_playwright() as p: # 连接到已经打开的、开启了调试端口的浏览器/应用 browser await p.chromium.connect_over_cds(http://127.0.0.1:9222) # 获取第一个页面通常是微信的主界面 pages browser.contexts[0].pages if browser.contexts else [] if pages: page pages[0] print(f成功连接到微信页面标题{await page.title()}) # 这里可以尝试进行一些简单操作例如点击通讯录 # await page.click(这里需要替换为通讯录按钮的选择器) else: print(未找到页面请检查微信是否已启动并登录。) await browser.close() asyncio.run(main())运行这个脚本如果能看到成功连接的提示说明环境搭建成功。4. 自动化脚本设计与核心环节实现环境就绪后我们开始设计核心的检测脚本。整个流程可以分为获取好友列表、遍历好友执行转账检测、记录结果。4.1 获取好友列表在微信PC版中好友列表通常在一个可滚动的div容器内每个好友对应一个列表项。我们可以通过Playwright定位这些元素来获取好友的备注名或微信号。async def get_friend_list(page): 获取当前通讯录列表中的所有好友名称 friend_list [] # 首先确保在通讯录页面。可以通过点击侧边栏的‘通讯录’图标实现。 # 这里需要你通过开发者工具F12手动定位‘通讯录’图标的选择器例如 # await page.click([aria-label通讯录]) # 定位好友列表容器 # 同样需要手动定位例如 # list_container page.locator(.friend-list-container) list_container page.locator(你通过工具查看到的好友列表容器的CSS选择器) # 获取所有好友项 friend_items list_container.locator(.friend-item) # 替换为实际的选择器 count await friend_items.count() for i in range(count): item friend_items.nth(i) # 提取好友显示名称可能需要定位内部的某个span或div name_element item.locator(.display-name) # 替换为实际的选择器 if await name_element.count() 0: name await name_element.text_content() friend_list.append(name.strip()) # 注意微信列表是虚拟滚动可能需要滚动才能加载更多元素 # 可以加入滚动逻辑if i % 10 0: await page.mouse.wheel(0, 300) return friend_list实操心得微信PC版的DOM结构并不对外公开且可能随版本更新而变化。因此所有选择器如.friend-list-container都需要你使用浏览器开发者工具在微信窗口按F12手动审查元素来获取。这是UI自动化中最耗时但也最核心的一步。选择器要尽量选择具有唯一性、稳定性的属性例如>async def check_single_friend(page, friend_name): 检测单个好友状态 try: # 1. 点击搜索框输入好友名 search_box page.locator(#searchInput) # 替换为实际搜索框选择器 await search_box.click() await search_box.fill(friend_name) await page.wait_for_timeout(1000) # 等待搜索结果出现 # 2. 在搜索结果中点击该好友打开聊天窗口 # 定位搜索结果列表中的对应项并点击 search_result_item page.locator(f.search-result-item:has-text({friend_name})).first if await search_result_item.count() 0: print(f未找到好友: {friend_name}) return friend_name, NOT_FOUND await search_result_item.click() await page.wait_for_timeout(1500) # 等待聊天窗口加载 # 3. 在聊天窗口中找到‘转账’按钮并点击 # 需要先点击聊天输入框旁的‘’号弹出菜单 more_btn page.locator(.chat-toolbox-more) # 替换为实际选择器 await more_btn.click() await page.wait_for_timeout(500) # 在弹出菜单中点击‘转账’ transfer_menu_item page.locator(text转账).first # 使用文本定位 await transfer_menu_item.click() await page.wait_for_timeout(1000) # 等待转账界面弹出 # 4. 关键判断是否存在“你不是收款方好友”的提示 # 定位提示框的文本或元素 error_dialog page.locator(text你不是收款方好友).or_(page.locator(text对方不是你的朋友)) # 设置一个较短的超时快速判断元素是否存在 try: await error_dialog.wait_for(statevisible, timeout2000) # 如果找到了错误提示框说明被删除 print(f{friend_name}: 疑似已删除) # 关闭提示框和转账窗口 close_btn page.locator(.dialog-close) # 替换为实际关闭按钮选择器 await close_btn.click() await page.wait_for_timeout(500) return friend_name, DELETED except Exception as e: # 没有等到错误提示框说明可能是正常好友 print(f{friend_name}: 好友关系正常) # 也需要关闭转账窗口点击取消或右上角X cancel_btn page.locator(button:has-text(取消)).first await cancel_btn.click() await page.wait_for_timeout(500) return friend_name, NORMAL except Exception as e: print(f检测好友 {friend_name} 时发生错误: {e}) return friend_name, ERROR finally: # 5. 清理关闭聊天窗口回到主界面清空搜索框 close_chat_btn page.locator(.chat-header-close) # 替换为实际选择器 await close_chat_btn.click() await page.wait_for_timeout(500) # 清空搜索框 search_box page.locator(#searchInput) await search_box.click() await page.keyboard.press(ControlKeyA) await page.keyboard.press(Delete) await page.wait_for_timeout(300)4.3 主流程整合与优化将上述函数整合并添加一些优化措施如延迟控制、结果保存等。import asyncio import csv from datetime import datetime from playwright.async_api import async_playwright async def main(): results [] async with async_playwright() as p: browser await p.chromium.connect_over_cds(http://127.0.0.1:9222) if not browser.contexts: print(未找到任何页面请检查连接。) return page browser.contexts[0].pages[0] print(正在获取好友列表...) # 这里调用之前定义的 get_friend_list 函数 friends await get_friend_list(page) # 假设我们已经有了这个函数 # 为了测试可以先使用一个小的好友列表 # friends [张三, 李四, 测试好友] total len(friends) print(f共找到 {total} 个好友开始检测...) for idx, friend in enumerate(friends, 1): print(f[{idx}/{total}] 正在检测: {friend}) name, status await check_single_friend(page, friend) results.append((name, status)) # 非常重要的延迟避免操作过快被微信识别为异常行为 await page.wait_for_timeout(3000) # 每个好友检测间隔3秒 # 保存结果到CSV文件 filename fwechat_check_result_{datetime.now().strftime(%Y%m%d_%H%M%S)}.csv with open(filename, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([好友昵称, 状态]) writer.writerows(results) print(f检测完成结果已保存至: {filename}) print(统计) deleted [r for r in results if r[1] DELETED] print(f 疑似被删除: {len(deleted)} 人) for d in deleted: print(f - {d[0]}) await browser.close() if __name__ __main__: asyncio.run(main())5. 常见问题、风险规避与排查技巧实录在实际开发和运行过程中你一定会遇到各种各样的问题。下面是我踩过坑后总结出来的经验。5.1 元素定位失败最常见问题现象脚本报错提示找不到某个元素TimeoutError或Element not found。原因与排查选择器失效微信更新导致DOM结构变化。这是最主要的原因。解决重新使用开发者工具F12审查元素更新脚本中的CSS选择器或文本定位。尽量使用包含唯一标识的稳定属性。页面未加载完成操作执行得太快元素还没渲染出来。解决在关键操作后增加await page.wait_for_timeout(毫秒数)或使用await page.wait_for_selector(‘选择器’)等待元素出现。页面状态不对例如你以为在聊天窗口但实际上焦点还在搜索框。解决在脚本中增加更多的状态检查和日志输出。例如在执行转账前先判断当前页面标题或某个特定元素是否存在。技巧编写一个通用的safe_click或safe_fill函数内部包含重试和等待逻辑可以大大提高脚本的健壮性。async def safe_click(page, selector, max_retries3): for i in range(max_retries): try: await page.wait_for_selector(selector, statevisible, timeout5000) await page.click(selector) return True except Exception as e: print(f点击 {selector} 失败第{i1}次重试。错误: {e}) await page.wait_for_timeout(1000) print(f点击 {selector} 彻底失败。) return False5.2 操作频率过快导致异常现象脚本运行一段时间后微信界面可能卡住、无响应或者弹出安全验证如滑块验证。原因微信有完善的反自动化机制高频、规律的操作极易被识别为非人类行为。规避策略增加随机延迟不要使用固定的间隔。可以在操作之间加入await page.wait_for_timeout(2000 random.randint(-500, 1000))这样的随机等待。模拟人类操作在点击、输入等操作中加入微小的移动轨迹变化可以使用page.mouse.move(x, y)模拟更自然的鼠标移动。控制检测规模一次不要检测太多好友可以分批次、分天进行。我们的示例脚本中设置了3秒的固定间隔这已经比较保守但你仍然可以根据情况调整。准备“熔断”机制脚本中应监控是否出现验证码页面。一旦发现立即暂停脚本并通知用户进行手动干预。5.3 法律与合规风险重申这是最重要的一部分必须单独强调。用户协议微信用户协议明确禁止任何形式的自动化登录、爬取、干扰服务正常运行的行为。本实战项目仅供个人学习和技术研究严禁用于任何商业用途、批量骚扰或对微信服务造成压力。隐私边界你检测的只能是你自己账号的好友列表。任何尝试获取非本人好友信息的行为都是非法的。账号风险频繁的自动化操作可能导致你的微信账号被限制功能如无法拉群、无法转账甚至被封号。请使用不重要的“小号”进行测试和学习。技术伦理我们学习技术是为了创造价值解决问题而不是制造问题。请将此类技术应用于正当的自动化测试、辅助工具开发等场景。5.4 脚本的优化与扩展方向如果你已经成功实现了基础功能可以考虑以下方向进行优化多线程/异步优化虽然我们用了异步asyncio但UI操作本质是顺序的。真正的并行检测需要操作多个微信客户端实例复杂度激增且风险更大不推荐。状态持久化与断点续查将已检测的好友状态和进度保存到文件或数据库。如果脚本中途中断下次可以从断点处继续避免重复检测。更友好的结果展示将CSV结果生成可视化图表或集成到简单的本地Web页面中查看。异常状态细化除了“删除”还有“被拉黑”、“对方账号异常”等状态可以通过解析不同的提示文本来进行更精细的判断。整个项目从环境搭建到脚本调试最耗时的部分无疑是元素定位和应对微信客户端的更新变化。这要求开发者有足够的耐心和前端调试能力。通过这个实战你学到的绝不仅仅是如何检测微信好友更是如何系统性地分析一个桌面应用、设计自动化方案以及处理各种边界情况的能力。技术永远在更新但解决问题的思路和方法论是相通的。