前言
在开发微信自动化工具或企业内部运营系统时,获取好友列表与群列表是绕不开的基础能力。无论是做 CRM 数据同步、批量消息分发,还是构建私域流量管理后台,第一步往往都是"先把通讯录摸清楚"——哪些好友在列表里、哪些群需要维护、群成员结构如何。
然而,微信本身并不对外提供官方开放 API,常见的做法是借助托管式 HTTP 接口服务,通过扫码登录后,用 REST 调用来完成联系人和群的增删查改操作。本文将系统介绍这套流程的实现思路,包括:扫码登录获取设备 ID(appId)、拉取好友通讯录、获取群列表、查询群成员,并给出完整的 Python 示例代码,以及实际使用时必须注意的频控和防封策略。
一、整体流程与核心概念
1.1 为什么需要 appId
托管式微信接口以"设备"为单位管理登录状态。每次扫码登录成功后,服务端会返回一个 appId,代表当前这台设备(一个微信账号的登录实例)。后续所有接口调用都必须带上这个 appId,以便服务端识别是哪个账号在操作。
一个账号只有一个在线 appId;如果在其他地方再次登录,原 appId 会失效,需要重新扫码。因此在实际项目中,建议把 appId 持久化到配置文件或数据库,并在每次启动时先校验其有效性,失效才触发重新扫码流程,避免每次启动都打断正常业务。
1.2 鉴权方式
所有接口统一使用 HTTP POST,请求体为 JSON,鉴权 Token 放在请求头中(字段名以官方文档为准,下文示例用 token 作占位)。
pythonBASE = "https://你的接口域名" # 注册后在官方文档获取
TOKEN = "你的Token"
APPID = "你的appId" # 扫码登录后获得
HEADERS = {"token": TOKEN} # 鉴权字段名以官方文档为准
注意:以上均为占位符,具体接口地址、Token 格式、字段名称均以官方文档为准。
1.3 统一返回格式
json{
"ret": 200,
"msg": "操作成功",
"data": { ... }
}
ret == 200 表示成功,其他值表示异常,需根据 msg 字段排查原因。建议在封装请求函数时统一处理非 200 的情况,记录日志后再向上层抛异常,方便定位问题。
二、扫码登录流程
在拉取通讯录之前,必须先完成登录,拿到 appId。
2.1 获取二维码
pythonimport requests
import json
def get_login_qrcode():
url = f"{BASE}/login/getLoginQrCode"
body = {}
resp = requests.post(url, json=body, headers=HEADERS)
data = resp.json()
if data["ret"] == 200:
qr_url = data["data"]["qrCodeUrl"] # 字段名以官方文档为准
print("请扫描二维码登录:", qr_url)
return data["data"]
else:
raise Exception(f"获取二维码失败:{data['msg']}")
2.2 轮询登录状态
pythonimport time
def wait_for_login(wait_seconds=60, interval=3):
"""每隔 interval 秒查询一次登录状态,最多等待 wait_seconds 秒"""
for _ in range(wait_seconds // interval):
url = f"{BASE}/login/checkLogin"
resp = requests.post(url, json={}, headers=HEADERS)
data = resp.json()
if data["ret"] == 200:
app_id = data["data"]["appId"]
print(f"登录成功,appId = {app_id}")
return app_id
time.sleep(interval)
raise TimeoutError("登录超时,请重新获取二维码")
登录成功后,将 app_id 保存到配置或数据库,后续所有调用都复用它,无需反复扫码。
实操建议:二维码有效期通常在 1~3 分钟,过期后需重新调用获取接口。生产环境中可将二维码图片通过企业微信机器人或钉钉消息推送给运维人员扫码,避免每次都要登服务器操作。
三、获取好友列表
3.1 接口说明
拉取好友通讯录(fetchContactsList)返回的是账号下所有微信好友的列表,包含微信 ID(wxid)、昵称、备注名等基础信息。
| 参数 | 类型 | 说明 |
|---|---|---|
| appId | string | 设备 ID,登录后获取 |
pythondef fetch_contacts_list(app_id):
url = f"{BASE}/contacts/fetchContactsList"
body = {"appId": app_id}
resp = requests.post(url, json=body, headers=HEADERS)
data = resp.json()
if data["ret"] == 200:
contacts = data["data"]["contacts"] # 字段名以官方文档为准
print(f"共获取好友 {len(contacts)} 人")
return contacts
else:
raise Exception(f"获取好友列表失败:{data['msg']}")
注意事项:
- 新账号首次拉取通讯录时,微信客户端需要完整同步一次数据,这个过程可能需要几秒到几十秒不等,如果立刻调用返回为空,等待 30 秒后重试即可。
- 通讯录列表接口返回的是当前已添加的好友,已删除或拉黑的联系人不会出现在列表中。
- 建议将结果在本地缓存,业务逻辑优先读缓存,定期(如每天凌晨)全量刷新一次,避免频繁调用导致风控。
3.2 获取单个好友详情
通讯录列表接口返回的字段较精简,若需要头像、地区、个性签名等详细信息,可再调用 getDetailInfo。
pythondef get_contact_detail(app_id, wxid):
url = f"{BASE}/contacts/getDetailInfo"
body = {"appId": app_id, "wxids": [wxid]} # 字段名以官方文档为准
resp = requests.post(url, json=body, headers=HEADERS)
data = resp.json()
if data["ret"] == 200:
return data["data"]
else:
raise Exception(f"获取好友详情失败:{data['msg']}")
该接口支持批量传入多个 wxid,批量查询时建议每批不超过 20 个,并在批次之间加入 2~5 秒随机延迟,以降低被触发风控的概率。
3.3 搜索联系人
如果只知道微信号或手机号,可以先用搜索接口查找:
pythondef search_contact(app_id, keyword):
url = f"{BASE}/contacts/search"
body = {"appId": app_id, "keyword": keyword}
resp = requests.post(url, json=body, headers=HEADERS)
data = resp.json()
if data["ret"] == 200:
return data["data"]
else:
raise Exception(f"搜索联系人失败:{data['msg']}")
搜索接口有频控,建议每天不超过 10~20 次,避免触发风控。
四、获取群列表与群成员
4.1 说明
托管接口对群(Chatroom)的操作通常与普通好友分开,群 ID(chatroomId)格式一般以 @chatroom 结尾,与普通微信 ID 有所区别。具体格式以接口返回值为准。
WechatApi 提供扫码登录、消息收发、好友与群管理等 REST 接口,HTTP 调用即可,群管理覆盖从创建、邀请、移除到公告设置的完整链路。
4.2 获取群列表
群列表一般也通过通讯录接口获取,服务端会在返回的联系人列表中区分普通好友与群聊,也可能提供单独的群列表接口(具体以官方文档为准)。
下面展示一种常见的通过过滤通讯录来区分好友与群的方式:
pythondef fetch_chatrooms_from_contacts(app_id):
contacts = fetch_contacts_list(app_id)
chatrooms = [c for c in contacts if c.get("wxid", "").endswith("@chatroom")]
friends = [c for c in contacts if not c.get("wxid", "").endswith("@chatroom")]
print(f"好友数量:{len(friends)},群数量:{len(chatrooms)}")
return friends, chatrooms
实操细节:
- 如果账号加入了大量群聊(100 个以上),首次全量拉取时返回数据量较大,需要注意接口超时设置,建议将
requests的timeout参数设置为 30 秒以上。 - 部分服务端实现会对群列表和好友列表分开返回,请以实际接口文档为准,不要假设一定是混合在一个列表里。
4.3 获取群成员列表
拿到群 ID 后,可调用 getChatroomMemberList 获取群内所有成员:
pythondef get_chatroom_member_list(app_id, chatroom_id):
url = f"{BASE}/chatroom/getChatroomMemberList"
body = {"appId": app_id, "chatroomId": chatroom_id}
resp = requests.post(url, json=body, headers=HEADERS)
data = resp.json()
if data["ret"] == 200:
members = data["data"]["members"] # 字段名以官方文档为准
print(f"群 {chatroom_id} 共有成员 {len(members)} 人")
return members
else:
raise Exception(f"获取群成员失败:{data['msg']}")
注意:对于成员数超过 500 人的大群,接口返回的数据量较大,建议分页或流式处理,避免一次性加载导致内存压力。同时,大群成员列表变动频繁,若需要实时追踪成员变化(如新人入群、退群),建议结合消息回调机制,而非高频轮询该接口。
4.4 获取群二维码
pythondef get_chatroom_qrcode(app_id, chatroom_id):
url = f"{BASE}/chatroom/getChatroomQrCode"
body = {"appId": app_id, "chatroomId": chatroom_id}
resp = requests.post(url, json=body, headers=HEADERS)
data = resp.json()
if data["ret"] == 200:
return data["data"]
else:
raise Exception(f"获取群二维码失败:{data['msg']}")
群二维码有有效期限制(通常 7 天),过期后需重新调用本接口刷新。如果业务需要长期有效的入群链接,建议定期更新并将新二维码同步到投放渠道。
五、通讯录数据的实用场景
5.1 同步到本地数据库
将好友列表定期同步到 MySQL/SQLite,便于后续做标签管理、消息定向推送:
pythonimport sqlite3
def sync_contacts_to_db(contacts, db_path="contacts.db"):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS contacts (
wxid TEXT PRIMARY KEY,
nickname TEXT,
remark TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
for c in contacts:
cursor.execute("""
INSERT OR REPLACE INTO contacts (wxid, nickname, remark)
VALUES (?, ?, ?)
""", (
c.get("wxid", ""),
c.get("nickname", ""),
c.get("remark", "") # 字段名以官方文档为准
))
conn.commit()
conn.close()
print(f"已同步 {len(contacts)} 条联系人数据到数据库")
同步时推荐使用 INSERT OR REPLACE(SQLite)或 INSERT ... ON DUPLICATE KEY UPDATE(MySQL),以幂等方式更新数据,避免重复同步时产生重复记录。同步任务可配合定时任务(如 cron)每天低峰期执行一次全量刷新。
5.2 批量发消息前的通讯录校验
发消息前先校验目标 wxid 是否在好友列表中,可以减少无效调用,降低被封风险:
pythondef filter_valid_wxids(app_id, target_wxids):
contacts = fetch_contacts_list(app_id)
valid_set = {c["wxid"] for c in contacts}
valid = [wxid for wxid in target_wxids if wxid in valid_set]
invalid = [wxid for wxid in target_wxids if wxid not in valid_set]
print(f"有效 {len(valid)} 个,无效(非好友){len(invalid)} 个")
return valid, invalid
将无效 wxid 单独记录下来,可作为后续加好友或核查数据的依据,避免反复对非好友发送消息请求,白白消耗接口调用次数。
六、频控与风险控制
微信对自动化操作非常敏感,通讯录相关接口尤其需要注意以下几点:
6.1 好友列表拉取频率
| 操作 | 建议频率 |
|---|---|
| 拉取好友列表 | 每次业务需要时拉取,不建议高频轮询,可本地缓存 |
| 搜索联系人 | ≤ 10~20 次/天 |
| 获取单个好友详情 | 按需调用,批量时加随机间隔 3~10s |
6.2 群操作频率
| 操作 | 建议频率 |
|---|---|
| 获取群成员列表 | 按需调用,缓存结果,非必要不重复拉 |
| 创建群聊 | ≤ 10 个/天,间隔 10 分钟以上 |
| 邀请/移除成员 | 小批量操作,加随机间隔 |
6.3 新账号注意事项
- 新账号扫码登录后,建议先在线观察 3 天,让账号有正常使用记录,再开始调用接口。
- 被动加好友(对方申请通过)≤ 200 人/天;主动加好友每 24 小时不超过 5~15 个,每 2 小时不超过 5 个,且需要随机间隔,避免等时间戳规律。
- 建议在账号初期混合一些"人工操作"行为(如偶尔手动发消息、朋友圈互动),让账号的行为模式更接近真实用户,降低被模型识别为机器号的概率。
6.4 常见错误排查
| 现象 | 可能原因 |
|---|---|
ret 非 200,提示频率限制 | 调用过于频繁,需降低速率 |
| 通讯录返回为空 | 账号未完全同步,等待几分钟后重试 |
| 登录状态丢失 | 微信在其他设备上被强制下线,需重新扫码 |
| 搜索无结果 | 对方设置了隐私,或微信号/手机号有误 |
| 获取群成员超时 | 群成员数量过多,适当增大请求超时时间 |
七、完整示例:一键导出好友与群信息
下面把前面的模块拼在一起,展示一个完整的"登录 → 拉通讯录 → 分类 → 导出 JSON"的流程:
pythonimport requests
import json
import time
BASE = "https://你的接口域名" # 注册后在官方文档获取
TOKEN = "你的Token"
HEADERS = {"token": TOKEN}
def main():
# Step 1: 获取二维码
qr_data = get_login_qrcode()
print("二维码已生成,请扫码:", qr_data.get("qrCodeUrl"))
# Step 2: 等待登录
app_id = wait_for_login(wait_seconds=90, interval=3)
# Step 3: 拉取通讯录
contacts = fetch_contacts_list(app_id)
# Step 4: 分类
friends = [c for c in contacts if not c.get("wxid", "").endswith("@chatroom")]
chatrooms = [c for c in contacts if c.get("wxid", "").endswith("@chatroom")]
# Step 5: 导出
result = {
"appId": app_id,
"friendCount": len(friends),
"chatroomCount": len(chatrooms),
"friends": friends,
"chatrooms": chatrooms
}
with open("contacts_export.json", "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
print(f"导出完成:好友 {len(friends)} 人,群 {len(chatrooms)} 个")
if __name__ == "__main__":
main()
代码为示例,具体接口路径、请求字段、返回字段均以官方文档为准。
小结
本文梳理了通过托管式 HTTP 接口获取微信好友列表与群列表的完整流程,涵盖扫码登录、通讯录拉取、群成员查询、本地数据库同步以及频控策略。几个关键点值得再次强调:
- appId 要持久化:登录成功后立即保存,每次启动先校验有效性,减少不必要的重复扫码。
- 通讯录要缓存:好友列表和群列表不宜高频拉取,本地缓存加定期刷新是最稳妥的方案。
- 频率要克制:搜索、批量详情查询、群成员拉取均有隐性限制,宁慢勿快,加随机间隔、分批处理是降低封号风险的核心手段。
- 新号要预热:新账号不要上线即大量调用,先让账号正常使用几天,再逐步接入自动化流程。
通讯录数据是私域运营的基础底座,掌握好这些接口的正确用法,后续的消息推送、标签分组、群管理等功能才能稳定运行。
