[531]微信之wxpy库(基于itchat库)

微信机器人 / 可能是最优雅的微信个人号 API

wxpy 在 itchat 的基础上,通过大量接口优化提升了模块的易用性,并进行丰富的功能扩展

注意

强烈建议仅使用小号运行机器人!

从近期 (17年6月下旬) 反馈来看,使用机器人存在一定概率被限制登录的可能性。

主要表现为无法登陆 Web 微信 (但不影响手机等其他平台)。

项目主页

https://github.com/youfou/wxpy

用来干啥

一些常见的场景

  • 控制路由器、智能家居等具有开放接口的玩意儿
  • 运行脚本时自动把日志发送到你的微信
  • 加群主为好友,自动拉进群中
  • 跨号或跨群转发消息
  • 自动陪人聊天
  • 逗人玩

总而言之,可用来实现各种微信个人号的自动化操作

轻松安装

wxpy 支持 Python 3.4-3.6,以及 2.7 版本

将下方命令中的 “pip” 替换为 “pip3” 或 “pip2”,可确保安装到对应的 Python 版本中

  1. 从 PYPI 官方源下载安装 (在国内可能比较慢或不稳定):
pip install -U wxpy
  1. 从豆瓣 PYPI 镜像源下载安装 (推荐国内用户选用):
pip install -U wxpy -i "https://pypi.doubanio.com/simple/"

简单上手

登陆微信:

# 导入模块
from wxpy import *
# 初始化机器人,扫码登陆
bot = Bot()

找到好友:

# 搜索名称含有 "游否" 的男性深圳好友
my_friend = bot.friends().search('游否', sex=MALE, city="深圳")[0]

发送消息:

# 发送文本给好友
my_friend.send('Hello WeChat!')
# 发送图片
my_friend.send_image('my_picture.jpg')

自动响应各类消息:

# 打印来自其他好友、群聊和公众号的消息
@bot.register()
def print_others(msg):
    print(msg)

# 回复 my_friend 的消息 (优先匹配后注册的函数!)
@bot.register(my_friend)
def reply_my_friend(msg):
    return 'received: {} ({})'.format(msg.text, msg.type)

# 自动接受新的好友请求
@bot.register(msg_types=FRIENDS)
def auto_accept_friends(msg):
    # 接受好友请求
    new_friend = msg.card.accept()
    # 向新的好友发送消息
    new_friend.send('哈哈,我自动接受了你的好友请求')

保持登陆/运行:

# 进入 Python 命令行、让程序保持运行
embed()

# 或者仅仅堵塞线程
# bot.join()

模块特色

  • 全面对象化接口,调用更优雅

  • 默认多线程响应消息,回复更快

  • 包含 聊天机器人、共同好友 等 实用组件

  • 只需两行代码,在其他项目中 用微信接收警告

  • 愉快的探索和调试,无需涂涂改改

  • 可混合使用 itchat 的原接口

  • 当然,还覆盖了各类常见基本功能:

    • 发送文本、图片、视频、文件
    • 通过关键词或用户属性搜索 好友、群聊、群成员等
    • 获取好友/群成员的昵称、备注、性别、地区等信息
    • 加好友,建群,邀请入群,移出群

更新日志

https://github.com/youfou/wxpy/releases


机器人对象

机器人 Bot 对象可被理解为一个 Web 微信客户端。

注解

关于发送消息,请参见聊天对象。

关于消息对象和自动处理,请参见 消息处理。

初始化/登陆

注解

Bot在初始化时便会执行登陆操作,需要手机扫描登陆。

class wxpy.Bot(cache_path=None, console_qr=False, qr_path=None, qr_callback=None, login_callback=None, logout_callback=None)

机器人对象,用于登陆和操作微信账号,涵盖大部分 Web 微信的功能:

from wxpy import *
bot = Bot()

# 机器人账号自身
myself = bot.self

# 向文件传输助手发送消息
bot.file_helper.send('Hello from wxpy!')

参数:

  • cache_path
    • 设置当前会话的缓存路径,并开启缓存功能;为 None (默认) 则不开启缓存功能。
    • 开启缓存后可在短时间内避免重复扫码,缓存失效时会重新要求登陆。
    • 设为 True 时,使用默认的缓存路径 ‘wxpy.pkl’。
  • console_qr
    • 在终端中显示登陆二维码,需要安装 pillow 模块 (pip3 install pillow)。
    • 可为整数(int),表示二维码单元格的宽度,通常为 2 (当被设为 True 时,也将在内部当作 2)。
    • 也可为负数,表示以反色显示二维码,适用于浅底深字的命令行界面。
    • 例如: 在大部分 Linux 系统中可设为 True 或 2,而在 macOS Terminal 的默认白底配色中,应设为 -2。
  • qr_path – 保存二维码的路径
  • qr_callback – 获得二维码后的回调,可以用来定义二维码的处理方式,接收参数: uuid, status, qrcode
  • login_callback – 登陆成功后的回调,若不指定,将进行清屏操作,并删除二维码文件
  • logout_callback – 登出时的回调

Bot.enable_puid(path=‘wxpy_puid.pkl’)

可选操作: 启用聊天对象的 puid 属性:

# 启用 puid 属性,并指定 puid 所需的映射数据保存/载入路径
bot.enable_puid('wxpy_puid.pkl')

# 指定一个好友
my_friend = bot.friends().search('游否')[0]

# 查看他的 puid
print(my_friend.puid)
# 'edfe8468'

小技巧

puidwxpy 特有的聊天对象/用户ID

不同于其他 ID 属性,puid 可始终被获取到,且具有稳定的唯一性

参数: path – puid 所需的映射数据保存/载入路径

Bot.auto_mark_as_read

为 True 时,将自动消除手机端的新消息小红点提醒 (默认为 False)

获取聊天对象

Bot.self

机器人自身 (作为一个聊天对象)

若需要给自己发送消息,请先进行以下一次性操作:

# 在 Web 微信中把自己加为好友
bot.self.add()
bot.self.accept()

# 发送消息给自己
bot.self.send('能收到吗?')

Bot.file_helper

文件传输助手

Bot.friends(update=False)

获取所有好友

参数: update – 是否更新
返回: 聊天对象合集
返回类型: wxpy.Chats

Bot.groups(update=False, contact_only=False)

获取所有群聊对象

一些不活跃的群可能无法被获取到,可通过在群内发言,或修改群名称的方式来激活

参数:

  • update – 是否更新
  • contact_only – 是否限于保存为联系人的群聊

返回: 群聊合集

返回类型: wxpy.Groups

Bot.mps(update=False)

获取所有公众号

参数: update – 是否更新
返回: 聊天对象合集
返回类型:wxpy.Chats

Bot.chats(update=False)

获取所有聊天对象

参数: update – 是否更新
返回: 聊天对象合集
返回类型:wxpy.Chats

搜索聊天对象

注解

  • 通过 .search() 获得的搜索结果 均为列表
  • 若希望找到唯一结果,可使用 ensure_one()

搜索好友:

# 搜索名称包含 '游否' 的深圳男性好友
found = bot.friends().search('游否', sex=MALE, city='深圳')
# []
# 确保搜索结果是唯一的,并取出唯一结果
youfou = ensure_one(found)
# 

搜索群聊:

# 搜索名称包含 'wxpy',且成员中包含 `游否` 的群聊对象
wxpy_groups = bot.groups().search('wxpy', [youfou])
# [, ]

在群聊中搜素:

# 在刚刚找到的第一个群中搜索
group = wxpy_groups[0]
# 搜索该群中所有浙江的群友
found = group.search(province='浙江')
# [, ,  ...]

搜索任何类型的聊天对象 (但不包含群内成员)

# 搜索名称含有 'wxpy' 的任何聊天对象
found = bot.search('wxpy')
# [, , ]

加好友和建群

Bot.add_friend(user, verify_content=’’)

添加用户为好友

参数:

  • user – 用户对象,或 user_name
  • verify_content – 验证说明信息

Bot.add_mp(user)

添加/关注 公众号

参数: user – 公众号对象,或 user_name

Bot.accept_friend(user, verify_content=’’)

接受用户为好友

参数:

  • user – 用户对象或 user_name
  • verify_content – 验证说明信息

返回:

新的好友对象

返回类型: wxpy.Friend

自动接受好友请求:

# 注册好友请求类消息
@bot.register(msg_types=FRIENDS)
# 自动接受验证信息中包含 'wxpy' 的好友请求
def auto_accept_friends(msg):
    # 判断好友请求中的验证文本
    if 'wxpy' in msg.text.lower():
        # 接受好友 (msg.card 为该请求的用户对象)
        new_friend = bot.accept_friend(msg.card)
        # 或 new_friend = msg.card.accept()
        # 向新的好友发送消息
        new_friend.send('哈哈,我自动接受了你的好友请求')

Bot.create_group(users, topic=None)

创建一个新的群聊

参数:

  • users – 用户列表 (不含自己,至少 2 位)
  • topic – 群名称

返回: 若建群成功,返回一个新的群聊对象

返回类型: wxpy.Group

其他

Bot.user_details(user_or_users, chunk_size=50)

获取单个或批量获取多个用户的详细信息(地区、性别、签名等),但不可用于群聊成员

参数:

  • user_or_users – 单个或多个用户对象或 user_name
  • chunk_size – 分配请求时的单批数量,目前为 50

返回: 单个或多个用户用户的详细信息

Bot.upload_file(path)

上传文件,并获取 media_id

可用于重复发送图片、表情、视频,和文件

参数: path – 文件路径
返回: media_id
返回类型: str

Bot.join()

堵塞进程,直到结束消息监听 (例如,机器人被登出时)

Bot.logout()

登出当前账号

控制多个微信 (多开)

仅需初始化多个 Bot 对象,即可同时控制多个微信:

bot1 = Bot()
bot2 = Bot()

聊天对象

通过机器人对象 Botchats(), friends()groups(), mps() 方法, 可分别获取到当前机器人的 所有聊天对象、好友、群聊,以及公众号列表。

而获得到的聊天对象合集 ChatsGroups具有一些合集方法,例如:Chats.search()可用于按条件搜索聊天对象:

from wxpy import *
bot = Bot()
my_friend = bot.friends().search('游否', sex=MALE, city='深圳')[0]
# 

在找到好友(或其他聊天对象)后,还可使用该聊天对象的 send系列方法,对其发送消息:

# 发送文本
my_friend.send('Hello, WeChat!')
# 发送图片
my_friend.send_image('my_picture.png')
# 发送视频
my_friend.send_video('my_video.mov')
# 发送文件
my_friend.send_file('my_file.zip')
# 以动态的方式发送图片
my_friend.send('@img@my_picture.png')

各类型的继承关系

在继续了解各个聊天对象之前,我们需要首先 理解各种不同类型聊天对象的继承关系

基础类

所有聊天对象,均继承于以下两种基础类,并拥有相应的属性和方法。

基本聊天对象 Chat

  • 所有的聊天对象均继承于此类型
  • 拥有 微信ID、昵称 等属性
  • 拥有 发送消息 Chat.send(), 获取头像 Chat.get_avatar() 等方法

单个聊天对象 User

  • 继承于 Chat,表示个体聊天对象 (而非群聊)。

  • 被以下聊天对象所继承

    • 好友对象 Friend
    • 群成员对象 Member
    • 公众号对象 MP
  • 拥有 性别、省份、城市、是否为好友 等属性

  • 拥有 加为好友 User.add(), 接受为好友 User.accept()等方法

实际类

在实际使用过程中,我们会更多的用到以下实际聊天对象类型。

小技巧

请牢记,除了自身私有的属性和方法外,它们还拥有对应基础类的属性和方法 (未重复列出)。

  • 好友 Friend
  • 群聊 Group
  • 群成员 Member
  • 公众号 MP

注解

阅读以下内容,你将了解:

  • 如何获取他们的各种属性 (ID、昵称、性别、地区、是否为好友关系等)
  • 如何对他们进行发送消息、加为好友、加入群聊、下载头像 等操作

基本聊天对象

所有聊天对象都继承于”基本聊天对象”,并拥有相应的属性和方法。

class wxpy.Chat(raw, bot)

单个用户 (User) 和群聊 (Group) 的基础类

bot

所属的 机器人对象

raw

原始数据

puid

持续有效,且稳定唯一的聊天对象/用户ID,适用于持久保存

请使用 Bot.enable_puid() 来启用 puid 属性

小技巧

puidwxpy 特有的聊天对象/用户ID

不同于其他 ID 属性,puid 可始终被获取到,且具有稳定的唯一性

注意

puid 映射数据 不可跨机器人使用

nick_name

该聊天对象的昵称 (好友、群员的昵称,或群名称)

name

该聊天对象的友好名称

具体为: 从 备注名称、群聊显示名称、昵称(或群名称),或微信号中

按序选取第一个可用的

send(content=None, media_id=None)

动态发送不同类型的消息,具体类型取决于 msg 的前缀。

参数:

  • content
    • 前缀内容 两个部分组成,若 省略前缀,将作为纯文本消息发送
    • 前缀 部分可为: ‘@fil@’, ‘@img@’, ‘@msg@’, ‘@vid@’ (不含引号)
    • 分别表示: 文件,图片,纯文本,视频
    • 内容 部分可为: 文件、图片、视频的路径,或纯文本的内容
  • media_id – 填写后可省略上传过程

返回类型: wxpy.SentMessage

send_msg(msg=None)

发送文本消息

参数: msg – 文本内容
返回类型: wxpy.SentMessage

send_image(path, media_id=None)

发送图片

参数:

  • path – 文件路径
  • media_id – 设置后可省略上传

返回类型: wxpy.SentMessage

send_file(path, media_id=None)

发送文件

参数:

  • path – 文件路径
  • media_id – 设置后可省略上传

返回类型: wxpy.SentMessage

send_video(path=None, media_id=None)

发送视频

参数:

  • path – 文件路径
  • media_id – 设置后可省略上传

返回类型: wxpy.SentMessage

send_raw_msg(raw_type, raw_content, uri=None, msg_ext=None)

以原始格式发送其他类型的消息。

参数:

  • raw_type (int) – 原始的整数消息类型
  • raw_content (str) – 原始的消息内容
  • uri (str) – 请求路径,默认为 ‘/webwxsendmsg’
  • msg_ext (dict) – 消息的扩展属性 (会被更新到 Msg 键中)

返回类型: wxpy.SentMessage

例如,发送好友或公众号的名片:

my_friend.send_raw_msg(
    # 名片的原始消息类型
    raw_type=42,
    # 注意 `username` 在这里应为微信 ID,且被发送的名片必须为自己的好友
    raw_content=''
)

mark_as_read()

消除当前聊天对象的未读提示小红点

pin()

将聊天对象置顶

unpin()

取消聊天对象的置顶状态

get_avatar(save_path=None)

获取头像

参数: save_path – 保存路径(后缀通常为.jpg),若为 None 则返回字节数据

uin

微信中的聊天对象ID,固定且唯一

因微信的隐私策略,该属性有时无法被获取到

建议使用 puid 作为用户的唯一 ID

alias

若用户进行过一次性的 “设置微信号” 操作,则该值为用户设置的”微信号”,固定且唯一

因微信的隐私策略,该属性有时无法被获取到

建议使用 puid 作为用户的唯一 ID

wxid

聊天对象的微信ID (实际为 .alias 或 .uin)

因微信的隐私策略,该属性有时无法被获取到

建议使用 puid 作为用户的唯一 ID

user_name

该聊天对象的内部 ID,通常不需要用到

注意

同个聊天对象在不同用户中,此 ID 不一致 ,且可能在新会话中 被改变

单个聊天对象

class wxpy.User(raw, bot)

好友(Friend)、群聊成员(Member),和公众号(MP) 的基础类

remark_name

备注名称

set_remark_name(remark_name)

设置或修改好友的备注名称

参数: remark_name – 新的备注名称

sex

性别,目前有:

# 男性
MALE = 1
# 女性
FEMALE = 2

未设置时为 None

province

省份

city

城市

signature

个性签名

is_friend

判断当前用户是否为好友关系

返回: 若为好友关系,返回对应的好友,否则返回 False

add(verify_content=’’)

把当前用户加为好友

参数: verify_content – 验证信息(文本)

accept(verify_content=’’)

接受当前用户为好友

参数: verify_content – 验证信息(文本)
返回: 新的好友对象
返回类型: wxpy.Friend

好友

class wxpy.Friend(raw, bot)

好友对象

群聊

class wxpy.Group(raw, bot)

群聊对象

members

群聊的成员列表

search(keywords=None, **attributes)

在群聊中搜索成员

注解

搜索结果为一个 Chats (列表)对象

建议搭配 ensure_one()使用

参数:

  • keywords – 成员名称关键词
  • attributes – 属性键值对

返回: 匹配的群聊成员

返回类型: wxpy.Chats

owner

返回群主对象

is_owner

判断所属 bot 是否为群管理员

self

机器人自身 (作为群成员)

update_group(members_details=False)

更新群聊的信息

参数: members_details – 是否包括群聊成员的详细信息 (地区、性别、签名等)

add_members(users, use_invitation=False)

向群聊中加入用户

参数:

  • users – 待加入的用户列表或单个用户
  • use_invitation – 使用发送邀请的方式

remove_members(members)

从群聊中移除用户

参数: members – 待移除的用户列表或单个用户

rename_group(name)

修改群聊名称

参数: name – 新的名称,超长部分会被截断 (最长32字节)

群成员

class wxpy.Member(raw, group)

群聊成员对象

display_name

在群聊中的显示昵称

remove()

从群聊中移除该成员

name

该群成员的友好名称

具体为: 从 群聊显示名称、昵称(或群名称),或微信号中,按序选取第一个可用的

实用技巧

判断一位用户是否在群中只需用 in语句:

friend = bot.friends().search('游否')[0]
group = bot.groups().search('wxpy 交流群')[0]

if friend in group:
    print('是的,{} 在 {} 中!'.format(friend.name, group.name))
    # 是的,游否 在 wxpy 交流群 中!

若要遍历群成员,可直接对群对象使用 for 语句:

# 打印所有群成员
for member in group:
    print(member)

若需查看群成员数量,直接使用 len() 即可:

len(group) # 这个群的成员数量

若需判断一位群成员是否就是某个好友,使用 == 即可:

member = group.search('游否')[0]
if member == friend:
    print('{} is {}'.format(member, friend))
    #  is 

公众号

class wxpy.MP(raw, bot)

公众号对象

聊天对象合集

好友、公众号、群聊成员的合集

Chats 对象中,除了最常用到的 search()外,还有两个特别的方法,stats()stats_text(),可用来统计好友或群成员的性别和地区分布:

bot.friends().stats_text()
# 游否 共有 100 位微信好友\n\n男性: 67 (67.0%)\n女性: 23 (23.0%) ...

class wxpy.Chats(chat_list=None, source=None)

多个聊天对象的合集,可用于搜索或统计

search(keywords=None, **attributes)

在聊天对象合集中进行搜索

注解

搜索结果为一个 Chats (列表)对象

建议搭配 ensure_one()使用

参数:

  • keywords – 聊天对象的名称关键词
  • attributes – 属性键值对,键可以是 sex(性别), province(省份), city(城市) 等。例如可指定 province=’广东’

返回: 匹配的聊天对象合集

返回类型: wxpy.Chats

stats(attribs=(‘sex’, ‘province’, ‘city’))

统计各属性的分布情况

参数: attribs – 需统计的属性列表或元组
返回: 统计结果

stats_text(total=True, sex=True, top_provinces=10, top_cities=10)

简单的统计结果的文本

参数:

  • total – 总体数量
  • sex – 性别分布
  • top_provinces – 省份分布
  • top_cities – 城市分布

返回: 统计结果文本

add_all(interval=3, verify_content=’’)

将合集中的所有用户加为好友,请小心应对调用频率限制!

参数:

  • interval – 间隔时间(秒)
  • verify_content – 验证说明文本
群聊的合集

class wxpy.Groups(group_list=None)

群聊的合集,可用于按条件搜索

search(keywords=None, users=None, **attributes)

在群聊合集中,根据给定的条件进行搜索

参数:

  • keywords – 群聊名称关键词
  • users – 需包含的用户
  • attributes – 属性键值对,键可以是 owner(群主对象), is_owner(自身是否为群主), nick_name(精准名称) 等。

返回: 匹配条件的群聊列表

返回类型:wxpy.Groups


消息处理

每当机器人接收到消息时,会自动执行以下两个步骤

  1. 将消息保存到 Bot.messages
  2. 查找消息预先注册的函数,并执行(若有匹配的函数)

消息对象

消息对象代表每一条从微信获取到的消息。

基本属性

Message.type

消息的类型,目前可为以下值:

# 文本
TEXT = 'Text'
# 位置
MAP = 'Map'
# 名片
CARD = 'Card'
# 提示
NOTE = 'Note'
# 分享
SHARING = 'Sharing'
# 图片
PICTURE = 'Picture'
# 语音
RECORDING = 'Recording'
# 文件
ATTACHMENT = 'Attachment'
# 视频
VIDEO = 'Video'
# 好友请求
FRIENDS = 'Friends'
# 系统
SYSTEM = 'System'

返回类型: str

Message.bot

接收此消息的 机器人对象

Message.id

消息的唯一 ID (通常为大于 0 的 64 位整型)

内容数据

Message.text

消息的文本内容

Message.get_file(save_path=None)

下载图片、视频、语音、附件消息中的文件内容。

可与 Message.file_name 配合使用。

参数: save_path – 文件的保存路径。若为 None,将直接返回字节数据

Message.file_name

消息中文件的文件名

Message.file_size

消息中文件的体积大小

Message.media_id

文件类消息中的文件资源 ID (但图片视频语音等其他消息中为空)

Message.raw

原始数据 (dict 数据)

用户相关

Message.chat

消息所在的聊天会话,即:

  • 对于自己发送的消息,为消息的接收者
  • 对于别人发送的消息,为消息的发送者

返回类型: wxpy.User, wxpy.Group

Message.sender

消息的发送者

返回类型: wxpy.User, wxpy.Group

Message.receiver

消息的接收者

返回类型: wxpy.User, wxpy.Group

Message.member

  • 若消息来自群聊,则此属性为消息的实际发送人(具体的群成员)
  • 若消息来自其他聊天对象(非群聊),则此属性为 None

返回类型: NoneType, wxpy.Member

Message.card

  • 好友请求中的请求用户
  • 名片消息中的推荐用户
群聊相关

Message.member

  • 若消息来自群聊,则此属性为消息的实际发送人(具体的群成员)
  • 若消息来自其他聊天对象(非群聊),则此属性为 None

返回类型: NoneType, `wxpy.Member

Message.is_at

当消息来自群聊,且被 @ 时,为 True

时间相关

Message.create_time

服务端发送时间

Message.receive_time

本地接收时间

Message.latency

消息的延迟秒数 (发送时间和接收时间的差值)

其他属性

Message.url

分享类消息中的网页 URL

Message.articles

公众号推送中的文章列表 (首篇的 标题/地址 与消息中的 text/url 相同)

其中,每篇文章均有以下属性:

  • title: 标题
  • summary: 摘要
  • url: 文章 URL
  • cover: 封面或缩略图 URL

Message.location

位置消息中的地理位置信息

Message.img_height

图片高度

Message.img_width

图片宽度

Message.play_length

视频长度

Message.voice_length

语音长度

回复方法

Message.reply(…)

等同于 Message.chat.send(...)

Message.reply_image(…)

等同于 Message.chat.send_image(...)

Message.reply_file(…)

等同于 Message.chat.send_file(...)

Message.reply_video(…)

等同于 Message.chat.send_video(...)

Message.reply_msg(…)

等同于 Message.chat.send_msg(...)

Message.reply_raw_msg(…)

等同于 Message.chat.send_raw_msg(...)

转发消息

Message.forward(chat, prefix=None, suffix=None, raise_for_unsupported=False)

将本消息转发给其他聊天对象

支持以下消息类型

  • 文本 (TEXT)

  • 视频(VIDEO)

  • 文件 (ATTACHMENT)

  • 图片/自定义表情 (PICTURE)

    • 但不支持表情商店中的表情
  • 名片 (CARD)

    • 仅支持公众号名片,以及自己发出的个人号名片
  • 分享 (SHARING)

    • 会转化为 标题 + 链接 形式的文本消息
  • 语音 (RECORDING)

    • 会以文件方式发送
  • 地图 (MAP)

    • 会转化为 位置名称 + 地图链接 形式的文本消息

参数:

  • chat (Chat) – 接收转发消息的聊天对象

  • prefix (str) – 转发时增加的 前缀 文本,原消息为文本时会自动换行

  • suffix (str) – 转发时增加的 后缀 文本,原消息为文本时会自动换行

  • raise_for_unsupported (bool) –

    为 True 时,将为不支持的消息类型抛出 NotImplementedError 异常

例如,将公司群中的老板消息转发出来:

from wxpy import *

bot = Bot()

# 定位公司群
company_group = ensure_one(bot.groups().search('公司微信群'))

# 定位老板
boss = ensure_one(company_group.search('老板大名'))

# 将老板的消息转发到文件传输助手
@bot.register(company_group)
def forward_boss_message(msg):
    if msg.member == boss:
        msg.forward(bot.file_helper, prefix='老板发言')

# 堵塞线程
embed()

自动处理消息

可通过 预先注册 的方式,实现消息的自动处理。

“预先注册” 是指预先将特定聊天对象的特定类型消息,注册到对应的处理函数,以实现自动回复等功能

注册消息

提示

每当收到新消息时,将根据注册规则找到匹配条件的执行函数。

并将 消息对象 作为唯一参数传入该函数。

Bot.register()作为函数的装饰器,即可完成注册。

# 打印所有*群聊*对象中的*文本*消息
@bot.register(Group, TEXT)
def print_group_msg(msg):
    print(msg)

注意

优先匹配 后注册 的函数,且仅匹配 一个 注册函数。

Bot.register(chats=None, msg_types=None, except_self=True, run_async=True, enabled=True)

装饰器:用于注册消息配置

参数:

  • chats – 消息所在的聊天对象:单个或列表形式的多个聊天对象或聊天类型,为空时匹配所有聊天对象
  • msg_types – 消息的类型:单个或列表形式的多个消息类型,为空时匹配所有消息类型 (SYSTEM 类消息除外)
  • except_self – 排除由自己发送的消息
  • run_async – 是否异步执行所配置的函数:可提高响应速度
  • enabled – 当前配置的默认开启状态,可事后动态开启或关闭

小技巧

  1. chats 和 msg_types 参数可以接收一个列表或干脆一个单项。按需使用,方便灵活。
  2. chats 参数既可以是聊天对象实例,也可以是对象类。当为类时,表示匹配该类型的所有聊天对象。
  3. 在被注册函数中,可以通过直接 return <回复内容>的方式来回复消息,等同于调用 msg.reply(<回复内容>)。
开始运行

注解

在完成注册操作后,若没有其他操作,程序会因主线程执行完成而退出。

因此务必堵塞线程以保持监听状态!

wxpy 的 embed()可在堵塞线程的同时,进入 Python 命令行,方便调试,一举两得。

from wxpy import *

bot = Bot()

@bot.register()
def print_messages(msg):
    print(msg)

# 堵塞线程,并进入 Python 命令行
embed()

wxpy.embed(local=None, banner=’’, shell=None)

进入交互式的 Python 命令行界面,并堵塞当前线程

支持使用 ipython, bpython 以及原生 python

参数:

  • shell (str) –

    指定命令行类型,可设为 ‘ipython’,’bpython’,’python’,或它们的首字母;

    若为 None,则按上述优先级进入首个可用的 Python 命令行。

  • local (dict) – 设定本地变量环境,若为 None,则获取进入之前的变量环境。

  • banner (str) – 设定欢迎内容,将在进入命令行后展示。

示例代码

在以下例子中,机器人将

  • 忽略 “一个无聊的群” 的所有消息
  • 回复好友 “游否” 和其他群聊中被 @ 的 TEXT 类消息
  • 打印所有其他消息

初始化机器人,并找到好友和群聊:

from wxpy import *
bot = Bot()
my_friend = bot.friends().search('游否')[0]
boring_group = bot.groups().search('一个无聊的群')[0]

打印所有其他消息:

@bot.register()
def just_print(msg):
    # 打印消息
    print(msg)

回复好友”游否”和其他群聊中被 @ 的 TEXT 类消息:

@bot.register([my_friend, Group], TEXT)
def auto_reply(msg):
    # 如果是群聊,但没有被 @,则不回复
    if isinstance(msg.chat, Group) and not msg.is_at:
        return
    else:
        # 回复消息内容和类型
        return '收到消息: {} ({})'.format(msg.text, msg.type)

忽略”一个无聊的群”的所有消息:

@bot.register(boring_group)
def ignore(msg):
    # 啥也不做
    return

堵塞线程,并进入 Python 命令行:

embed()
动态开关注册配置

注解

该操作需要在额外的线程中进行!

查看当前的注册配置情况:

bot.registered
# [,
#  ,
#  ]

关闭所有注册配置:

bot.registered.disable()

重新开启 just_print 函数:

bot.registered.enable(just_print)

查看当前开启的注册配置:

bot.registered.enabled
# []

class wxpy.api.messages.Registered(bot)

保存当前机器人所有已注册的消息配置

参数: bot – 所属的机器人

get_config(msg)

获取给定消息的注册配置。每条消息仅匹配一个注册配置,后注册的配置具有更高的匹配优先级。

参数: msg – 给定的消息
返回: 匹配的回复配置

get_config_by_func(func)

通过给定的函数找到对应的注册配置

参数: func – 给定的函数
返回: 对应的注册配置

enable(func=None)

开启指定函数的对应配置。若不指定函数,则开启所有已注册配置。

参数: func – 指定的函数

disable(func=None)

关闭指定函数的对应配置。若不指定函数,则关闭所有已注册配置。

参数: func – 指定的函数

enabled

检查处于开启状态的配置

返回: 处于开启状态的配置

disabled

检查处于关闭状态的配置

返回: 处于关闭状态的配置

已发送消息

class wxpy.SentMessage(attributes)

程序中通过 .send/reply() 系列方法发出的消息

使用程序发送的消息也将被记录到历史消息 bot.messages 中

提示

大部分属性与 Message 相同

recall()

撤回本条消息 (应为 2 分钟内发出的消息)

历史消息

可通过访问 bot.messages 来查看历史消息列表。

消息列表为 Messages对象,具有搜索功能。

例如,搜索所有自己在手机上发出的消息:

sent_msgs = bot.messages.search(sender=bot.self)
print(sent_msgs)

class wxpy.Messages(msg_list=None, max_history=200)

多条消息的合集,可用于记录或搜索

max_history

设置最大保存条数,即:仅保存最后的 n 条消息。

bot = Bot()
# 设置历史消息的最大保存数量为 10000 条
bot.messages.max_history = 10000

search(keywords=None, **attributes)

搜索消息记录

参数:

  • keywords – 文本关键词
  • attributes – 属性键值对

返回:所有匹配的消息

返回类型:wxpy.Messages

# 搜索所有自己发送的,文本中包含 'wxpy' 的消息
bot.messages.search('wxpy', sender=bot.self)

用微信监控你的程序

通过利用微信强大的通知能力,我们可以把程序中的警告/日志发到自己的微信上。

wxpy 提供以下两种方式来实现这个需求。

获得专用

wxpy.get_wechat_logger(receiver=None, name=None, level=30)

获得一个可向指定微信聊天对象发送日志的 Logger

参数:

  • receiver
    • 当为None, True 或字符串时,将以该值作为 cache_path 参数启动一个新的机器人,并发送到该机器人的”文件传输助手”
    • 当为 机器人 时,将发送到该机器人的”文件传输助手”
    • 当为 聊天对象 时,将发送到该聊天对象
  • name – Logger 名称
  • level – Logger 等级,默认为 logging.WARNING

返回: Logger

from wxpy import get_wechat_logger

# 获得一个专用 Logger
# 当不设置 `receiver` 时,会将日志发送到随后扫码登陆的微信的"文件传输助手"
logger = get_wechat_logger()

# 发送警告
logger.warning('这是一条 WARNING 等级的日志,你收到了吗?')

# 接收捕获的异常
try:
    1 / 0
except:
    logger.exception('现在你又收到了什么?')

加入到现有的 Logger

class wxpy.WeChatLoggingHandler(receiver=None)

可向指定微信聊天对象发送日志的 Logging Handler

参数:
receiver

  • 当为 None, True 或字符串时,将以该值作为 cache_path 参数启动一个新的机器人,并发送到该机器人的”文件传输助手”
  • 当为 机器人 时,将发送到该机器人的”文件传输助手”
  • 当为 聊天对象时,将发送到该聊天对象
import logging
from wxpy import WeChatLoggingHandler

# 这是你现有的 Logger
logger = logging.getLogger(__name__)

# 初始化一个微信 Handler
wechat_handler = WeChatLoggingHandler()
# 加到入现有的 Logger
logger.addHandler(wechat_handler)

logger.warning('你有一条新的告警,请查收。')

指定接收者

当然,我们也可以使用其他聊天对象来接收日志。

比如,先在微信中建立一个群聊,并在里面加入需要关注这些日志的人员。然后把这个群作为接收者。

from wxpy import *

# 初始化机器人
bot = Bot()
# 找到需要接收日志的群 -- `ensure_one()` 用于确保找到的结果是唯一的,避免发错地方
group_receiver = ensure_one(bot.groups().search('XX业务-告警通知'))

# 指定这个群为接收者
logger = get_wechat_logger(group_receiver)

logger.error('打扰大家了,但这是一条重要的错误日志...')

愉快的探索和调试

想要做点小试验,调试代码,或是探索 wxpy 的功能特性?反复修改和运行太麻烦。

试试下面两种玩法,告别涂涂改改的摸索方式。

使用 embed()

注解

适用于在现有的代码中进行探索和调试

只需将 embed() 放在代码中的任何位置。运行后,就可以从那儿开始探索和调试。

例如,初始化一个机器人,然后看看它能做些什么:

from wxpy import *
bot = Bot()
embed() # 进入 Python 命令行

# 输入对象名称并回车
>>> bot
# Out[1]: 
>>> bot.friends()
# Out[2]: [, , ]

wxpy.embed(local=None, banner=’’, shell=None)

进入交互式的 Python 命令行界面,并堵塞当前线程

支持使用 ipython, bpython 以及原生 python

参数:

  • shell (str) –

    指定命令行类型,可设为 ‘ipython’,’bpython’,’python’,或它们的首字母;

    若为 None,则按上述优先级进入首个可用的 Python 命令行。

  • local (dict) – 设定本地变量环境,若为 None,则获取进入之前的变量环境。

  • banner (str) – 设定欢迎内容,将在进入命令行后展示。

使用 wxpy 命令

注解

适用于在命令行中边写边探索

第二种情况:想要简单写几行,而不想创建脚本,那么使用 wxpy 命令行边写边探索,更方便。

在命令行中输入 wxpy -h 可快速查看使用说明。

选项
  • bot1 bot2 bot3…

    • 一个或多个需要初始化的机器人对象的名称,以空格分割
    • 默认:不初始化机器人
    • 例子: bot1 bot2
  • -c / –cache

    • 使用会话缓存功能,将创建 wxpy_*.pkl 缓存文件
    • 默认:不缓存会话
    • 例子:-c
  • -q 宽度 / –console_qr 宽度

    • 终端二维码的单元格宽度
    • 默认:不使用终端二维码
    • 例子:-q 2
  • -l 等级 / –logging_level 等级 (注意是小写 L,不是 I)

    • 日志等级
    • 默认:INFO
    • 例子:-l DEBUG
  • -s 交互界面 / –shell 交互界面

    • 选择所需使用的 Python 交互界面
    • 可为:ipython,bpython,python,或它们的首字母
    • 默认:以上首个可用的 Python 命令行
    • 例子:-s bpython
  • -v / –version

    • 展示版本信息并退出z
    • 例子:-v
例子

初始化一个名为 bot 的机器人:

wxpy bot

在此基础上,使用终端二维码,且单元格宽度为 2:

wxpy bot -q 2

分别初始化名为 bot1 和 bot2 的两个机器人:

wxpy bot1 bot2

在此基础上,使用会话缓存功能:

wxpy bot1 bot2 -c

在此基础上,指定使用 bpython:

wxpy bot1 bot2 -c -s bpython


实用组件

额外内置了一些实用的小组件,可按需使用。

聊天机器人

目前提供了以下两种自动聊天机器人接口。

图灵

class wxpy.Tuling(api_key=None)

与 wxpy 深度整合的图灵机器人

内置的 api key 存在调用限制,建议自行申请。

免费申请: http://www.tuling123.com/

参数: api_key – 你申请的 api key

bot = Bot()
my_friend = ensure_one(bot.search('游否'))
tuling = Tuling(api_key='你申请的 API KEY')

# 使用图灵机器人自动与指定好友聊天
@bot.register(my_friend)
def reply_my_friend(msg):
    tuling.do_reply(msg)

do_reply(msg, at_member=True)

回复消息,并返回答复文本

参数:

  • msg – Message 对象
  • at_member – 若消息来自群聊,回复时 @发消息的群成员

返回: 答复文本

返回类型:str

reply_text(msg, at_member=True)

仅返回消息的答复文本

参数:

  • msg – Message 对象
  • at_member – 若消息来自群聊,回复时 @发消息的群成员

返回: 答复文本

返回类型:str

小 i

class wxpy.XiaoI(key, secret)

与 wxpy 深度整合的小 i 机器人

需要通过注册获得 key 和 secret

免费申请: http://cloud.xiaoi.com/

参数:

  • key – 你申请的 key
  • secret – 你申请的 secret
bot = Bot()
my_friend = ensure_one(bot.search('寒风'))
xiaoi = XiaoI('你申请的 Key', '你申请的 Secret')

# 使用小 i 机器人自动与指定好友聊天
@bot.register(my_friend)
def reply_my_friend(msg):
    xiaoi.do_reply(msg)

do_reply(msg)

回复消息,并返回答复文本

参数: msg – Message 对象
返回: 答复文本

reply_text(msg)

仅返回答复文本

参数: msg – Message 对象,或消息文本
返回: 答复文本

查找共同好友

wxpy.mutual_friends(args)

找到多个微信用户的共同好友

参数: args – 每个参数为一个微信用户的机器人(Bot),或是聊天对象合集(Chats)
返回: 共同好友列表
返回类型: wxpy.Chats

bot1 = Bot()
bot2 = Bot()

# 打印共同好友
for mf in mutual_friends(bot, bot2):
    print(mf)

确保查找结果的唯一性

wxpy.ensure_one(found)

确保列表中仅有一个项,并返回这个项,否则抛出 ValueError 异常

通常可用在查找聊天对象时,确保查找结果的唯一性,并直接获取唯一项

参数: found – 列表
返回: 唯一项

bot = Bot()
# 确保只找到了一个叫"游否"的好友,并返回这个好友
my_friend = ensure_one(bot.search('游否'))
# 

在多个群中同步消息

wxpy.sync_message_in_groups(msg, groups, prefix=None, suffix=None, raise_for_unsupported=False, run_async=True)

将消息同步到多个微信群中

支持以下消息类型

  • 文本 (TEXT)
  • 视频(VIDEO)
  • 文件 (ATTACHMENT)
  • 图片/自定义表情 (PICTURE)
    • 但不支持表情商店中的表情
  • 名片 (CARD)
    • 仅支持公众号名片,以及自己发出的个人号名片
  • 分享 (SHARING)
    • 会被转化为 标题 + 链接 形式的纯文本
  • 语音 (RECORDING)
    • 会以文件方式发送
  • 地图 (MAP)
    • 会转化为位置名称 + 地图链接 形式的文本消息

参数:

  • msg (Message) – 需同步的消息对象

  • groups (Group) – 需同步的群列表

  • prefix (str) –

    • 转发时的 前缀 文本,原消息为文本时会自动换行
    • 若不设定,则使用默认前缀作为提示
  • suffix (str) –

    • 转发时的 后缀 文本,原消息为文本时会自动换行
    • 默认为空
  • raise_for_unsupported (bool) –

    为 True 时,将为不支持的消息类型抛出 NotImplementedError 异常

  • run_async (bool) – 是否异步执行,为 True 时不堵塞线程

my_groups = [group1, group2, group3 ...]

@bot.register(my_groups, except_self=False)
def sync_my_groups(msg):
    sync_message_in_groups(msg, my_groups)

检测频率限制

wxpy.detect_freq_limit(func, *args, **kwargs)

检测各类 Web 微信操作的频率限制,获得限制次数和周期

参数:

  • func – 需要执行的操作函数
  • args – 操作函数的位置参数
  • kwargs – 操作函数的命名参数

返回:限制次数, 限制周期(秒数)

例如,测试发送文本消息的频率限制:

bot = Bot('test.pkl')

# 定义需要检测的操作
def action():
    bot.file_helper.send()

# 执行检测
result = detect_freq_limit(action)
# 查看结果
print(result)
# (120, 120.111222333)

忽略 ResponseError 异常

wxpy.dont_raise_response_error(func)

装饰器:用于避免被装饰的函数在运行过程中抛出 ResponseError 错误


异常处理

异常的抛出和捕捉

每当使用 wxpy 向微信发出请求 (例如发送消息、加好友、建群等操作),wxpy 都会在收到服务端响应后进行检查。

若响应中的错误码不为 0,程序将抛出 ResponseError异常。

class wxpy.ResponseError(err_code, err_msg)

当 BaseResponse 的返回值不为 0 时抛出的异常

err_code

错误码 (int)

err_msg

错误消息 (文本),但可能为空

捕捉异常:

try:
    # 尝试向某个群员发送消息
    group.members[3].send('Hello')
except ResponseError as e:
    # 若群员还不是好友,将抛出 ResponseError 错误
    print(e.err_code, e.err_msg) # 查看错误号和错误消息

已知错误码

通常来说,每个错误码表示一种类型的错误。

但因微信未公开 (也没有义务公开) 这套错误码体系的具体说明,我们只能根据经验猜测部分错误码的定义。

以下为一些常见的已知错误码。欢迎提交 PR 进行完善。

1205

通常因为操作频率过高。需要控制频率,避免再次引起该错误。

注意

Web 微信对 加好友、建群 这两种操作的频率限制尤其严格!

对于微信而言,为了机器人避免打扰其他用户,以及控制服务器的负载压力,需要对各种不同的操作进行频率限制。

通常每种操作可有多层频率限制,而每层频率限制分为两个参数:

周期、次数,分布表示: 在 x 周期内,只能发送 y 个请求。

举个例子:

对于 发送消息 操作,可能会是这样 (数值为虚构):

限制周期 限制次数
1 2 分钟 120
2 10 分钟 300
3 1 小时 1000
4 24 小时 2000

可能会有用户在 1 分钟内狂发 100 条消息。

但这样的频率不可能维持一整天,所以一天内 3000 条是足够的。

通过以上方式,微信可实现较为合理的限制。

1204

通常因为操作对象不为好友关系。例如尝试向一位不为好友的群员发送消息时,会引起这个错误。

1100, 1101, 1102

通常表示机器人已经掉线,需要重新登录。

请重新初始化 Bot 对象,并重新注册消息。

因为重新登录后,聊天对象的 user_name 可能已经变化,所以原先的消息注册也会因此失效。


itchat 与原始数据

正是得益于 itchat 的坚实基础,wxpy 才能够在短时间内快速实现这些新的接口和功能。

感谢 itchat 维护者们的辛勤付出。

以下为如何在 wxpy 中混合使用 itchat 的原接口和原始数据。

使用 itchat 的原接口

只需在 wxpy 的 Bot对象后紧跟 .core.*即可调用 itchat 的原接口。

例如,使用 itchat 的search_friends 接口:

from wxpy import *
bot = Bot()
found = bot.core.search_friends('游否')

注意

通过 itchat 原接口所获取到的结果为原始数据,可能无法直接传递到 wxpy 的对应方法中。

使用原始数据

wxpy 的所有 聊天对象消息对象 均基于从 itchat 获取到的数据进行封装。若需使用原始数据,只需在对象后紧跟 .raw。

例如,查看一个 好友对象的原始数据:

from wxpy import *
bot = Bot()
a_friend = bot.friends()[0]
print(a_friend.raw)

必看: 常见问题 FAQ

提示

这里罗列了一些常见的问题,在提出新的问题前,请先看完本文。

启动后马上退出了?

因为主线程执行完成了,程序自然会退出。

只需在代码结尾加一句embed() 即可堵塞线程,还能进入 Python 命令行:

from wxpy import *

# 你的其他代码...

# 堵塞线程,并进入 Python 命令行
embed()

或者,也可以使用 Bot.join() 仅仅堵塞线程:

bot = Bot()

# 你的其他代码...

# 仅仅堵塞线程
bot.join()

# 机器人登出后会继续往下执行

每次登陆都要扫码?

可启用登陆状态缓存功能,在短时间内重新运行程序,可自动登录。

具体请见Bot 中的 cache_path 参数说明。

可以在 Linux 中使用吗?

wxpy 不依赖于图形界面,因此完全兼容各种纯终端的服务器。

但有一点需要注意,在纯终端环境中,登陆时必须使用”终端二维码”参数。

具体请见 Bot中的 console_qr 参数说明。

小技巧

遇到以下错误?请使用 Bot的 console_qr 参数。

FileNotFoundError: [Errno 2] No such file or directory: ‘xdg-open’

支持 红包、转账、朋友圈… 吗?

wxpy 使用了 Web 微信的通讯协议,因此仅能覆盖 Web 微信本身所具备的功能。

所以以下功能目前 均不支持

  • 支付相关 - 红包、转账、收款 等都不支持
  • 在群聊中@他人 - 是的,Web 微信中被人@后也不会提醒
  • 发送名片 - 但可以通过 send_raw_msg() 转发
  • 发送分享链接 - 也无法转发
  • 发送语音消息
  • 朋友圈相关

为什么要开发 wxpy?

wxpy 的初衷是帮助人们利用微信来使生活和工作更轻松。

注解

希望每位使用者在使用机器人时

  • 维护良好的交流环境
  • 永远不骚扰他人
  • 遵守法律和平台规则

wxpy文档:https://wxpy.readthedocs.io/zh/latest/

itchat文档:
https://itchat.readthedocs.io/zh/latest/
https://github.com/littlecodersh/ItChat

你可能感兴趣的:(爬虫进阶)