1. 为什么我要用 uiautomation 抓微信好友列表微信 PC 端本身不提供任何导出好友列表的入口通讯录管理页面虽然能看到全部联系人但只能一屏一屏往下滚手动复制粘贴几百个好友基本不现实。我最早是用截图 OCR 的方式做识别率受昵称里的特殊符号影响很大后来换成 uiautomation 直接读取控件树里的文本准确率一下就上来了。uiautomation 是 Python 的一个第三方库底层调用的是 Windows 的 UI Automation 接口能直接拿到窗口里每个控件的 Name、ClassName、ControlType 等属性。微信 PC 端的通讯录管理窗口是一个标准的 ListControl每个联系人就是一个 ListItemControl里面的昵称挂在 TextControl 上。我们要做的就是打开微信 → 进入通讯录管理 → 定位列表控件 → 循环滚动读取 → 去重后写入 txt。这套流程适合谁适合需要批量整理微信好友、做私域运营统计、或者单纯想备份一份好友名单的人。前提是你得在 Windows 上操作因为 uiautomation 只支持 Windows 平台。另外微信版本不同控件名称可能有细微差异我会在排障章节里给出定位方法。整个脚本的核心难点有三个一是滚动加载微信的列表是虚拟列表不滚动就不会渲染后面的联系人二是去重滚动过程中同一个联系人可能被重复读到三是判断到底目前没有特别可靠的 API 能直接判断列表是否滚到底我采用的是人工按空格键触发最后一轮全量读取的方式。在写脚本之前我先把凭证管理这块理顺了。因为脚本后续可能要调用一些模型接口做昵称清洗或者分类如果每个脚本都硬编码 Key维护起来很麻烦。我试过用 TaoToken 的统一 Key 通道来管理这些调用凭证一个 Key 走所有模型省得来回切换。下面先把这块配置讲清楚再进入 uiautomation 的实操。2. TaoToken 统一 Key 通道的前置配置TaoToken 是一个模型调用凭证的统一管理通道官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。它的作用是让你用一个 Key 就能调用多个模型不用为每个模型单独申请和配置密钥。对于我这种经常写自动化脚本、时不时要加个模型调用做后处理的人来说省了不少事。先说你需要在脚本里用到的三件套Base URL、API Key、Model ID。Base URL 固定填 https://taotoken.net/api API Key 在控制台创建Model ID 根据你要用的模型填比如 claude-sonnet-4-20250514 或者 gpt-4o 这类。这三个东西在后面的配置片段里会反复出现先记牢。创建 Key 的路径是登录后进入控制台找到 API Keys 页面点新建复制生成的 Key。这个 Key 只显示一次记得存好。如果你用的是 Claude Code 或者 Cline 这类工具配置方式略有不同但核心三件套是一样的。我建议把 Key 放在环境变量里而不是直接写在脚本中。Windows 下可以这样设置setx TAOTOKEN_API_KEY sk-你的实际Key设置完之后需要重开终端才能生效。然后在 Python 脚本里用 os.environ 读取import os api_key os.environ.get(TAOTOKEN_API_KEY) base_url https://taotoken.net/api model_id claude-sonnet-4-20250514如果你用的是 settings.json 或者 config.toml 这类配置文件格式如下。以 Claude Code 的 settings.json 为例路径通常在用户目录下的 .claude 文件夹里{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的实际Key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }注意 Base URL 后面不要加 /v1 或者其他路径直接就是 https://taotoken.net/api 。Model ID 要和你实际使用的模型对应填错了会报 model not found。API Key 如果泄露了去控制台删掉重新建一个就行不影响其他配置。配置好之后你可以先用一个最简单的请求验证通道是否通。Python 里用 requests 发一个 chat completions 请求import requests import os url https://taotoken.net/api/v1/chat/completions headers { Authorization: fBearer {os.environ.get(TAOTOKEN_API_KEY)}, Content-Type: application/json } data { model: claude-sonnet-4-20250514, messages: [{role: user, content: 回复ok}] } resp requests.post(url, headersheaders, jsondata, timeout30) print(resp.status_code) print(resp.json())如果返回 200 并且 choices 里有内容说明通道没问题。如果返回 401检查 Key 是否复制完整如果返回 404检查 URL 路径是否写对。这一步验证通过之后再回到 uiautomation 的主线任务。3. uiautomation 抓取微信好友列表的可复制配置现在进入正题。先安装 uiautomationpip install uiautomation安装完成后你需要知道微信的安装路径。在桌面微信图标上右键 → 打开文件所在的位置就能看到 WeChat.exe 的完整路径。把这个路径填到脚本里的 subprocess.Popen 中。下面是完整的配置片段我把它拆成几个部分讲。第一部分是启动微信和进入通讯录管理import subprocess import uiautomation as auto import time # 替换成你自己的微信路径 wechat_path rC:\Program Files (x86)\Tencent\WeChat\WeChat.exe subprocess.Popen(wechat_path) time.sleep(3) # 等微信窗口加载 wechat_window auto.WindowControl( searchDepth1, classNameWeChatMainWndForPC, Name微信 ) # 点击通讯录按钮 contact_btn wechat_window.ButtonControl(Name通讯录) contact_btn.Click() time.sleep(1) # 点击通讯录管理 admin_btn wechat_window.ButtonControl(Name通讯录管理) admin_btn.Click() time.sleep(1)这里有几个坑要注意。第一微信路径如果包含空格用 r 原始字符串避免转义问题。第二time.sleep 的时长根据你机器性能调整太短了窗口还没渲染出来就去找控件会报 NoneType。第三className 和 Name 必须和实际窗口一致可以用 auto.WindowControl 的 Inspect 工具查看。第二部分是定位通讯录管理窗口和列表控件comm_admin auto.WindowControl( Name通讯录管理, ClassNameContactManagerWindow ) # 把鼠标移到窗口中心让滚轮对列表生效 comm_admin.MoveCursorToMyCenter() time.sleep(0.5) list_ctrl comm_admin.ListControl(Name)MoveCursorToMyCenter 这行很关键。微信的通讯录管理窗口里联系人列表只占一部分区域如果鼠标不在列表上方滚轮事件不会作用到列表上。把鼠标移到窗口中心基本就落在列表区域了。第三部分是去重和写入 txt 的逻辑。我用一个列表 a 存已读到的昵称用计数器 b 记录数量用 flag 控制循环退出a [] b 1 flag True start_time time.time() print(f开始时间{start_time}) output_file wechat_friends.txt while flag: list_ctrl comm_admin.ListControl(Name) first_item list_ctrl.GetChildren()[0].TextControl() if first_item.Name not in a: print(b, first_item.Name) b 1 a.append(first_item.Name) with open(output_file, a, encodingutf-8) as f: f.write(first_item.Name \n) auto.WheelDown(waitTime0.01) if auto.IsKeyPressed(auto.Keys.VK_SPACE): print(到底了开始读取最后一批) for item in list_ctrl.GetChildren()[1:]: last_name item.TextControl() if last_name.Name not in a: print(b, last_name.Name) b 1 a.append(last_name.Name) with open(output_file, a, encodingutf-8) as f: f.write(last_name.Name \n) flag False end_time time.time() print(f运行时间{end_time - start_time}s) print(f共获取 {len(a)} 个好友)这里写入格式是每行一个昵称用 \n 换行。如果你想要 CSV 格式可以改成逗号分隔但昵称里可能本身有逗号所以用换行更安全。文件编码用 utf-8避免中文乱码。关于去重逻辑上面用的是if first_item.Name not in a这会导致同名好友被漏掉。如果你有多个同名好友建议改成判断是否与最近添加的昵称相同if not a or first_item.Name ! a[-1]: # 添加逻辑这样只跳过连续重复的不会漏掉中间隔开的同名好友。两种方式各有取舍根据你的好友情况选择。4. 运行验证与结果落盘检查脚本写完之后运行方式很简单python wechat_friends.py运行过程中你会看到控制台不断打印昵称和序号。当滚动到底部时手动按一下空格键脚本会读取当前列表里剩余的联系人然后退出循环。验证结果是否准确可以从三个方面检查。第一看控制台输出的总数和微信通讯录管理页面显示的联系人总数是否一致。微信通讯录管理页面底部会显示总人数对比一下就知道有没有漏。第二打开生成的 wechat_friends.txt检查是否有乱码、空行、重复行。可以用命令行快速统计# Windows PowerShell Get-Content wechat_friends.txt | Measure-Object -Line # 或者用 Python 统计 python -c print(len(open(wechat_friends.txt, encodingutf-8).readlines()))第三随机抽取几个昵称在微信里搜索确认是否存在。如果发现漏了大概率是滚动速度太快导致某些联系人没被渲染出来。可以把auto.WheelDown(waitTime0.01)里的 waitTime 调大一点比如 0.05 或 0.1给微信渲染留出时间。我实测下来500 个好友的列表滚动加读取大概需要 2 到 3 分钟。如果好友数量超过 1000建议把 waitTime 设成 0.05否则容易漏。另外运行脚本期间不要手动操作鼠标和键盘否则会干扰 uiautomation 的控件定位。如果你在脚本里集成了 TaoToken 做昵称后处理比如自动分类或者清洗特殊字符可以在写入 txt 之前加一步调用def clean_nickname(name): # 这里可以调用 TaoToken 的模型接口做清洗 # 简单示例去掉首尾空格和不可见字符 return name.strip().replace(\u200b, ) # 写入时 f.write(clean_nickname(first_item.Name) \n)模型调用的三件套还是 Base URL https://taotoken.net/api 、API Key 从环境变量读、Model ID 按需选。这样整个流程就是uiautomation 抓取 → 模型清洗 → 写入 txt一条龙。5. 常见报错与排查方法这一节列几个我踩过的坑基本都是真实报错。报错一AttributeError: NoneType object has no attribute Click原因窗口还没加载出来就去找控件或者 className/Name 写错了。排查方法在 subprocess.Popen 之后加长 time.sleep或者用 auto.WindowControl 的 searchDepth 调大一点。也可以用 Inspect.exe 工具查看微信窗口的实际 className 和 Name。报错二local proxy failed或连接超时如果你在脚本里调用了模型接口出现这个报错通常是网络配置问题。检查 Base URL 是否填的 https://taotoken.net/api 不要多加路径。如果公司网络有代理需要在 requests 里配置 proxies 参数或者设置 NO_PROXY 环境变量。报错三401 UnauthorizedAPI Key 无效或过期。去 TaoToken 控制台重新生成一个确认复制完整没有多余空格。环境变量设置后要重开终端才生效。报错四reading choices相关错误模型返回格式不符合预期通常是 Model ID 填错了。确认你填的 Model ID 在 TaoToken 支持的模型列表里。如果用的是 Claude Code检查 settings.json 里的 ANTHROPIC_MODEL 字段。报错五滚动后列表没有更新微信的虚拟列表在滚动后需要一点时间渲染。把 WheelDown 的 waitTime 调大或者在每次滚动后加一个 time.sleep(0.1)。另外确认鼠标确实在列表区域上方MoveCursorToMyCenter 之后不要再移动鼠标。报错六txt 文件中文乱码打开文件时没有指定 encodingutf-8。所有 open() 调用都要加上这个参数。如果已经生成了乱码文件用记事本打开另存为 UTF-8 编码即可。报错七OAuth 相关错误如果你用的是 Claude Code 的 OAuth 登录方式而不是 API Key可能会遇到 token 过期。建议改用 API Key 方式在 settings.json 里配置 ANTHROPIC_API_KEY 和 ANTHROPIC_BASE_URL这样更稳定。排查顺序建议先确认微信窗口能正常定位再确认列表控件能读到子元素最后确认写入文件没有权限问题。每一步都可以用 print 打印中间结果来定位。6. 凭证管理与脚本调用的后续衔接好友列表落盘之后如果你还想做进一步处理比如按标签分类、按昵称首字母排序、或者调用模型做智能分组就需要一个稳定的凭证通道。TaoToken 的统一 Key 在这里的价值就体现出来了你不需要为每个模型单独维护一套 Key一个 Key 走所有调用。具体操作路径先去控制台创建 API Key地址是 https://taotoken.net/api-keys 然后参考接入文档 https://taotoken.net/doc 配置你的脚本或工具。如果你用的是 Claude Code 做长期编码任务可以考虑 Coding Plan https://taotoken.net/coding-plan 适合需要持续调用模型的场景。如果只是想快速验证某个模型的效果用模型对话页面 https://taotoken.net/chat 就行。回到 uiautomation 脚本本身我建议把凭证读取和好友抓取分成两个模块。抓取模块只负责读控件、写 txt不关心模型调用后处理模块从 txt 读数据调用模型做清洗或分类再写回新文件。这样职责清晰调试也方便。最后给一个实用技巧如果你的微信好友经常变动可以把脚本设成每周跑一次输出文件按日期命名比如 wechat_friends_20250612.txt。然后在后处理模块里对比两次的差异就能知道谁新增了、谁删除了。这个用 Python 的 set 差集就能实现不需要额外工具。整个流程跑通之后你会发现 uiautomation 能做的事情远不止抓好友列表。通讯录管理页面里的标签、备注、群聊列表都可以用类似的方式读取。关键就是找到对应的控件然后循环读取子元素。多试几次熟悉了控件树的层级结构后面就是套模板的事了。