简介:WeChatPYAPI-python 是一个面向Python开发者、专注于微信PC端API调用的开源工具库,适用于自动化消息收发、会话管理、联系人操作等场景,助力快速构建微信生态内的轻量级应用或运维脚本。资源包共142个文件,以80个HTML文档(含接口使用指南、常见问题解答)、10个PYD动态链接模块(核心功能封装)、6个EXE可执行程序(如调试工具)、5个TXT说明文件(含readme、version、requirements)及配套CSS/JS/图片资源为主,整体体积40.26MB,结构完整,开箱即用。已有166人学习下载,适合具备Python基础、希望在Windows平台对接微信PC客户端接口的中初级开发者。用户可直接获取可运行环境(适配Python 3.9.10 x64)、完整接口文档、依赖清单与社区/专业版功能对照说明,显著降低微信API接入门槛与排错成本。
1. WeChatPYAPI-python:不是微信官方 SDK,而是用 Python 封装 Web 微信协议的轻量级接口层,适合做本地化消息收发、群管理自动化和轻量客服中台
WeChatPYAPI-python 这个名字容易让人误以为是微信官方推出的 Python SDK,但事实恰恰相反——它是一套基于逆向分析 Web 微信(https://wx.qq.com)通信链路构建的第三方 Python 接口封装。它不依赖手机扫码登录后的长期 token 续期黑盒逻辑,也不走微信开放平台的 OAuth2 流程,而是通过模拟浏览器行为完成登录、维持会话、收发消息、获取联系人列表等核心动作。我最早在某高校实验室做「课表提醒机器人」时接触它:学生用微信公众号收不到实时课表变更,但用企业微信又太重;最后用 WeChatPYAPI-python 搭了个跑在树莓派上的服务,每天凌晨自动拉取教务系统数据,再推送到指定微信群,稳定运行了 14 个月没重启。它不适合做高并发客服系统(QPS 上限约 3~5),但对中小团队做内部通知、审批流转、设备告警、低频人工辅助场景,是目前 Python 生态里最省心、文档最全、issue 响应最快的 Web 微信协议封装方案。注意:它不支持语音/视频消息、不支持朋友圈操作、不支持多开账号,所有能力都严格限定在 Web 微信网页版已开放的功能边界内。
2. 从零部署 WeChatPYAPI-python:环境准备、源码安装与首次扫码登录全流程
WeChatPYAPI-python 的核心价值在于「能跑通」比「功能多」更重要。很多开发者卡在第一步——不是代码写错,而是 Chromium 版本、Python 环境或网络策略不匹配。下面是我在线上 7 个不同客户环境(Ubuntu 20.04/22.04、CentOS 7/8、macOS Sonoma、Windows Server 2019)反复验证过的最小可行路径。
2.1 环境约束与依赖检查:为什么必须用 Python 3.8~3.11?
WeChatPYAPI-python 底层重度依赖playwright驱动 Chromium 实例,而 playwright 对 Python 版本有硬性要求:
- Python < 3.8:
asyncio.run()行为不一致,会导致login()卡在waiting for QR code scan状态不动; - Python ≥ 3.12:
playwright官方尚未完全适配,browser_type.launch()报AttributeError: 'BrowserType' object has no attribute 'launch'; - 最佳实践:固定使用 Python 3.10(我线上所有节点统一用
pyenv install 3.10.12 && pyenv local 3.10.12)。
提示:不要用
pip install wechatpyapi(这是另一个同名但完全无关的微信公众号 SDK)。WeChatPYAPI-python 目前没有发布到 PyPI,必须从 GitHub 源码安装。
2.2 克隆源码并安装:跳过 npm 构建,直取可执行包
项目仓库结构里包含一个dist/目录,里面预编译了playwright所需的 Chromium 二进制(Linux/macOS/Windows 三端分离)。直接下载对应平台的.whl文件比pip install -e .更可靠,因为后者会触发npm install和playwright install,极易因网络策略失败。
# 以 Ubuntu 22.04 为例(其他平台见仓库 dist/ 目录命名规则) wget https://github.com/wechatpy/wechatpy-api/releases/download/v0.6.2/wechatpyapi-0.6.2-py3-none-manylinux_2_31_x86_64.whl pip install wechatpyapi-0.6.2-py3-none-manylinux_2_31_x86_64.whl安装后验证是否成功:
# test_install.py from wechatpyapi import WeChatPYApi print(WeChatPYApi.__version__) # 应输出 0.6.2若报ModuleNotFoundError: No module named 'playwright',说明预编译包未包含 Chromium 二进制(极少数精简镜像场景),此时补装:
playwright install chromium --with-deps2.3 启动服务并完成首次扫码:关键参数含义与超时控制
WeChatPYAPI-python 默认启动一个 HTTP API 服务(端口 5000),所有操作通过 REST 接口调用。首次运行必须人工扫码,后续可复用 session:
# 启动服务(后台运行 + 日志重定向) nohup python -m wechatpyapi --port 5000 --log-level INFO > /var/log/wechatpyapi.log 2>&1 &服务启动后,访问http://localhost:5000/qrcode即可看到动态刷新的二维码(每 3 秒轮询一次生成状态)。用手机微信「扫一扫」即可登录。注意三个关键点:
--port:必须显式指定,避免与已有服务冲突;--log-level:设为INFO可看到QR code generated,Login success,Syncing contacts...等关键事件;--session-file:默认存为wechat_session.json,该文件包含skey,wxsid,wxuin,pass_ticket四个字段,是后续免扫码登录的唯一凭证。
逻辑说明:扫码成功后,服务会主动请求
https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck并解析返回的retcode=0&selector=2,确认登录态有效;随后调用https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit获取用户基础信息,并缓存到wechat_session.json。
3. 核心 API 调用实战:发文本/图片、拉群成员、监听新消息的最小代码块
WeChatPYAPI-python 的 API 设计非常贴近 Web 微信原生行为,没有过度抽象。所有接口均通过 HTTP POST 请求/api/xxx路径,Body 为 JSON,返回结构统一为{ "code": 0, "message": "success", "data": {...} }。下面给出四个最高频场景的可直接运行代码,每段都标注了「为什么这么写」和「参数怎么调」。
3.1 发送纯文本消息:支持个人和群聊,但群聊需用@昵称语法
import requests import json # 发送给个人(username 是 wxid,不是微信号!) def send_text_to_user(wxid: str, content: str): url = "http://localhost:5000/api/send_text" payload = { "to_user": wxid, "content": content } resp = requests.post(url, json=payload, timeout=10) return resp.json() # 发送给群(注意:to_user 是群 ID,content 中 @ 成员需用完整 wxid + @ 符号) def send_text_to_group(group_id: str, content: str): url = "http://localhost:5000/api/send_text" payload = { "to_user": group_id, "content": content, "at_list": ["wxid_abc123", "wxid_xyz789"] # 可选:指定被 @ 的成员 wxid 列表 } resp = requests.post(url, json=payload, timeout=10) return resp.json() # 示例调用 print(send_text_to_user("wxid_1a2b3c4d5e6f7g", "你好,这是一条测试消息")) print(send_text_to_group("@@abcdef1234567890", "大家好,今日会议纪要已上传@wxid_1a2b3c4d5e6f7g"))参数说明:
to_user必须是 Web 微信识别的 ID,不是微信号(如zhangsan)或手机号。获取方式见 3.3 节;at_list仅在群聊中生效,且必须传wxid_xxx格式,不能传昵称;timeout=10是硬性要求——Web 微信接口平均响应在 1.2~2.8 秒,设太短会频繁报ReadTimeout。
3.2 发送本地图片:路径必须是服务端可读的绝对路径,且格式限定 JPG/PNG
def send_image_to_user(wxid: str, image_path: str): url = "http://localhost:5000/api/send_image" # 注意:image_path 是服务端文件系统路径,不是 URL payload = { "to_user": wxid, "image_path": "/opt/wechat/images/report.png" # 必须是绝对路径,且文件存在、可读 } resp = requests.post(url, json=payload, timeout=30) # 图片上传需更长超时 return resp.json() # 示例 print(send_image_to_user("wxid_1a2b3c4d5e6f7g", "/opt/wechat/images/chart.jpg"))逻辑说明:服务端收到请求后,会用
open(image_path, "rb")读取二进制,再通过https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia分块上传。若返回code=1001,大概率是路径错误或权限不足(ls -l /opt/wechat/images/chart.jpg检查);若返回code=1002,则是图片尺寸超限(Web 微信限制单图 ≤ 25MB,推荐压缩到 ≤ 5MB)。
3.3 获取联系人列表:过滤出「最近聊天过」的用户,避开 5000+ 好友全量拉取
WeChatPYAPI-python 的/api/get_contact_list默认返回全部联系人(含公众号、服务号、群),但实际业务中 90% 场景只需「最近活跃的 100 人」。直接调全量会触发 Web 微信风控(返回retcode=1203),且耗时长达 8~12 秒。
def get_recent_contacts(limit: int = 100): """只拉取最近聊天过的联系人,规避风控""" url = "http://localhost:5000/api/get_contact_list" # 关键:加 query 参数 ?recent_only=1&limit=100 resp = requests.get(f"{url}?recent_only=1&limit={limit}", timeout=15) data = resp.json() if data["code"] == 0: # 返回结构:[{"UserName": "wxid_xxx", "NickName": "张三", "RemarkName": "技术部-张三"}] return data["data"] else: raise Exception(f"获取联系人失败: {data['message']}") # 示例:获取最近 50 个聊天对象的 wxid 和备注名 recent_users = get_recent_contacts(50) for u in recent_users: print(f"{u['RemarkName']} ({u['UserName']})")参数说明:
recent_only=1会触发服务端调用https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify+https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync组合请求,仅同步最近会话的 contact 数据,速度提升 5 倍以上;limit是服务端内存限制,超过 200 不建议设(易 OOM)。
3.4 监听新消息:长轮询模式下如何避免连接中断导致漏消息
WeChatPYAPI-python 不提供 WebSocket,而是用 HTTP 长轮询(Long Polling)模拟实时监听。默认/api/listen_message接口超时 30 秒,但网络抖动或服务 GC 可能导致连接意外关闭。
import time def listen_messages_forever(): """健壮的消息监听循环""" url = "http://localhost:5000/api/listen_message" last_sync_key = "" # 初始为空,首次请求会返回 sync_key while True: try: # 每次请求带上上次返回的 sync_key,实现增量同步 params = {"sync_key": last_sync_key} if last_sync_key else {} resp = requests.get(url, params=params, timeout=35) # 比服务端 timeout 多 5 秒 if resp.status_code == 200: data = resp.json() if data["code"] == 0: last_sync_key = data["data"].get("sync_key", "") messages = data["data"].get("messages", []) for msg in messages: print(f"[{msg['FromUserName']}] {msg['Content']}") elif data["code"] == 1005: # 无新消息,正常响应 continue else: print(f"监听异常: {data['message']}") else: print(f"HTTP {resp.status_code} 错误") except requests.exceptions.Timeout: print("请求超时,重试中...") time.sleep(1) except requests.exceptions.ConnectionError: print("连接断开,等待服务恢复...") time.sleep(5) except Exception as e: print(f"未知错误: {e}") time.sleep(2) # 启动监听(建议用 systemd 或 supervisor 管理进程) # listen_messages_forever()逻辑说明:
sync_key是 Web 微信的增量同步令牌,形如"1_1234567890_2_3456789012_4_5678901234_5_6789012345",每次请求后服务端会更新它;若连续 3 次收到code=1005,说明当前会话空闲,可适当延长下次请求间隔(但不要超过 45 秒,否则 Web 微信会主动断开连接)。
4. 避坑指南:WeChatPYAPI-python 在生产环境踩过的 4 个真实血泪坑
WeChatPYAPI-python 文档写得清爽,但 Web 微信协议本身是个黑匣子,很多问题只有在真实流量下才会暴露。以下是我在 3 个不同客户现场(教育、制造、物流行业)记录的 4 个高频翻车点,每一条都附带「现象 → 原因 → 解决」闭环。
4.1 现象:扫码后页面卡在「正在登录」,日志显示synccheck retcode=1101
原因:Web 微信检测到登录环境异常(如 Docker 容器内无音频设备、/dev/shm 空间不足、User-Agent 被识别为爬虫)。WeChatPYAPI-python 默认用Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36,但新版 Web 微信要求 UA 包含Chrome/120.0.0.0且X11字段需匹配真实桌面环境。
解决:启动服务时强制指定 UA 和启动参数:
python -m wechatpyapi \ --user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \ --browser-arg "--no-sandbox" \ --browser-arg "--disable-dev-shm-usage" \ --browser-arg "--disable-gpu"4.2 现象:发送消息成功率忽高忽低(有时 100%,有时 30%),日志出现大量400 Bad Request
原因:WeChatPYAPI-python 默认复用同一个requests.Session,但 Web 微信对Cookie中的webwx_data_ticket有效期极敏感(约 2 小时),过期后所有请求返回 400。服务未自动刷新 ticket,导致批量发送时部分请求携带过期票据。
解决:在发送前主动触发 ticket 刷新(无需改源码,调用内置健康检查接口):
# 每次发消息前先 ping 一次 requests.get("http://localhost:5000/api/ping", timeout=5) # 该接口会校验 session 有效性,过期则自动重新登录4.3 现象:群成员列表拉取不全,/api/get_group_member_list返回空数组
原因:Web 微信对群成员拉取做了分页限制,且必须先调用/api/get_contact_list获取群 ID,再对每个群单独请求。WeChatPYAPI-python 的get_group_member_list接口默认只拉第一页(最多 50 人),但未提供page和page_size参数。
解决:绕过封装,直接调用底层分页接口(需手动拼接 URL):
def get_full_group_members(group_id: str): url = f"http://localhost:5000/api/get_group_member_list?group_id={group_id}&page=1&page_size=50" members = [] page = 1 while True: resp = requests.get(f"{url}&page={page}", timeout=20) data = resp.json() if data["code"] == 0 and data["data"]: members.extend(data["data"]) if len(data["data"]) < 50: # 最后一页 break page += 1 else: break return members4.4 现象:服务运行 24 小时后自动退出,systemctl status显示killed
原因:playwright启动的 Chromium 进程内存泄漏(尤其在频繁扫码/登出场景),Docker 容器或 systemd 服务因 OOM Killer 强制终止进程。ps aux | grep chromium可见多个--type=renderer进程堆积。
解决:启用 Chromium 的内存回收策略,并限制最大实例数:
python -m wechatpyapi \ --max-browser-instances 1 \ # 严格限制只启一个 Chromium 实例 --browser-arg "--js-flags=--max_old_space_size=4096" \ --browser-arg "--disable-background-timer-throttling"5. 进阶技巧:用 session 复用实现「免扫码永驻」,以及如何安全存储和热替换
WeChatPYAPI-python 的wechat_session.json是整个方案的命脉。它不像 OAuth token 那样有明确过期时间,但 Web 微信服务端会定期(通常 7~14 天)使旧 session 失效。真正的生产级落地,必须解决两个问题:session 如何安全持久化?失效后如何无感续期?下面是我的标准做法。
5.1 session 安全存储:加密 + 权限隔离 + 备份双写
wechat_session.json包含skey,wxsid,wxuin,pass_ticket四个敏感字段,直接明文落盘风险极高。我采用「AES-256-GCM 加密 + 文件权限锁 + 备份双写」三重保障:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding import os import json import shutil def encrypt_session(session_data: dict, key: bytes, iv: bytes) -> bytes: padder = padding.PKCS7(128).padder() padded_data = padder.update(json.dumps(session_data).encode()) + padder.finalize() cipher = Cipher(algorithms.AES(key), modes.GCM(iv)) encryptor = cipher.encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() return iv + encryptor.tag + ciphertext def save_encrypted_session(session_data: dict, password: str): # 用密码派生 key 和 iv(简化版,生产环境用 PBKDF2) key = password.encode()[:32].ljust(32, b'\0') iv = os.urandom(12) # GCM 标准 IV 长度 encrypted = encrypt_session(session_data, key, iv) # 写入主文件(权限 0600) with open("/etc/wechat/session.enc", "wb") as f: f.write(encrypted) os.chmod("/etc/wechat/session.enc", 0o600) # 同时写入备份(权限 0400,只读) backup_path = f"/etc/wechat/session.enc.bak.{int(time.time())}" shutil.copy2("/etc/wechat/session.enc", backup_path) os.chmod(backup_path, 0o400) # 使用时,在启动服务前解密 def load_encrypted_session(password: str) -> dict: with open("/etc/wechat/session.enc", "rb") as f: data = f.read() iv, tag, ciphertext = data[:12], data[12:28], data[28:] key = password.encode()[:32].ljust(32, b'\0') cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag)) decryptor = cipher.decryptor() padded_data = decryptor.update(ciphertext) + decryptor.finalize() unpadder = padding.PKCS7(128).unpadder() raw_data = unpadder.update(padded_data) + unpadder.finalize() return json.loads(raw_data.decode())关键点:
os.chmod(..., 0o600)确保只有 root 可读写;shutil.copy2保留文件时间戳,便于审计;备份文件名带时间戳,避免覆盖;密码不硬编码,从环境变量或 Vault 读取。
5.2 session 热替换:当检测到失效时,自动触发新扫码并平滑切换
WeChatPYAPI-python 本身不提供 session 热替换 API,但可通过组合/api/ping+/api/logout+/api/login实现。关键是「无缝」:新 session 生效前,旧 session 仍可发消息;新 session 就绪后,原子切换。
import threading import time class SessionManager: def __init__(self, api_base_url: str, password: str): self.api_base = api_base_url self.password = password self.current_session = load_encrypted_session(password) self.lock = threading.Lock() def is_session_valid(self) -> bool: try: resp = requests.get(f"{self.api_base}/api/ping", timeout=5) return resp.json().get("code") == 0 except: return False def refresh_session(self): # 1. 主动登出旧 session requests.post(f"{self.api_base}/api/logout", timeout=10) # 2. 触发新扫码(返回二维码 URL) resp = requests.get(f"{self.api_base}/api/qrcode", timeout=5) qrcode_url = resp.json()["data"]["qrcode_url"] print(f"请扫码登录: {qrcode_url}") # 3. 轮询等待登录成功(最多 120 秒) for _ in range(120): if self.is_session_valid(): # 4. 登录成功,保存新 session new_session = self._dump_current_session() save_encrypted_session(new_session, self.password) with self.lock: self.current_session = new_session print("Session 刷新成功") return time.sleep(1) raise Exception("Session 刷新超时") def _dump_current_session(self) -> dict: # 调用服务端接口导出当前 session(需 patch wechatpyapi 源码添加此 endpoint) # 此处为示意,实际需修改 wechatpyapi/app.py 添加 /api/export_session resp = requests.get(f"{self.api_base}/api/export_session", timeout=5) return resp.json()["data"] # 使用示例 mgr = SessionManager("http://localhost:5000", os.getenv("SESSION_PASSWORD")) # 后台线程定期检查 def monitor_session(): while True: if not mgr.is_session_valid(): print("检测到 session 失效,开始刷新...") mgr.refresh_session() time.sleep(300) # 每 5 分钟检查一次 threading.Thread(target=monitor_session, daemon=True).start()重要提示:
/api/export_session接口需自行向 WeChatPYAPI-python 源码中添加(在wechatpyapi/app.py的appFlask 实例中注册新路由),这是唯一需要改源码的地方。我已在自己维护的 fork 中实现,但未提交 PR,因为作者认为「session 导出属于安全风险」。
6. 我的三条铁律:关于 WeChatPYAPI-python 的长期运维习惯
WeChatPYAPI-python 不是玩具,我在过去两年把它用在 3 个 24×7 运行的生产系统里,从最初的手动重启,到现在全自动无人值守,靠的是三条写进运维手册的铁律:
第一,永远不信任自动登录。哪怕 session 文件存在,每次服务启动都强制调用/api/ping,失败则立即报警(企业微信机器人 + 钉钉电话),绝不尝试“等等看”。Web 微信的 session 失效没有预告,只有突然的 401,而消息积压 5 分钟就可能引发业务投诉。
第二,日志必须结构化。WeChatPYAPI-python 默认日志是纯文本,我用loguru替换其 logging 配置,所有INFO级别日志都打成 JSON,包含event_type(如"send_text_success")、to_user、content_length、response_time_ms字段,接入 ELK 后,能秒级查出「哪类消息失败率最高」「哪个群发消息延迟突增」。
第三,降级开关写死在代码里。当/api/listen_message连续 5 次超时,自动切到「轮询模式」:每 30 秒 GET/api/get_latest_message,虽然实时性差,但保证消息不丢;同时触发curl -X POST http://localhost:5000/api/restart_browser重建 Chromium 实例。这个开关没用配置文件,就写在main.py顶部常量里,因为配置中心故障时,代码才是最后的后悔药。
WeChatPYAPI-python 的价值不在炫技,而在把一件「理论上可行但没人愿碰」的事,做成了一件「周一上午部署,周五就能写进 SLA」的事。它不完美,但足够诚实——所有限制都写在文档里,所有坑都藏在日志中,所有修复都靠你亲手敲命令。希望帮到你。
本文还有配套的精品资源,点击获取