Python 分析在德的中国程序员,告别 996?(2)
开发需求
- 获取群聊群成员信息
- 找出所有群昵称不符合标准的群友
- 随机抽取5人,在群里发布改昵称提醒消息
- 同时将这次提醒的5人,存储进数据库
- 每天早八点晚八点两次定时启动昵称检查脚本
- 某人在将来被提醒次数超过10次,还不予配合不改昵称时,将自动踢出群
- 新群友被邀请进入群时,立刻发送群规提示改昵称
开发分解
该任务所需第三方库如下:
pip3 install wxpy pip3 install apscheduler pip3 install pymysql pip3 install DBUtils
1. 建库建表
本文采用的是MySQL,后期可以扩展支持Postgre或者MongoDB。
因为需要存储微信表情字符集,所以表的默认编码采用utf8mb4_unicode_ci。
DROP TABLE IF EXISTS `wx_chat_group`; CREATE TABLE `wx_chat_group` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', PRIMARY KEY `id` (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci; INSERT INTO `wx_chat_group` (`id`, `name`) VALUES (1, '德国IT职业信息分享群'); -- 每次抽取的不合规格的昵称将存储如表以供计数 DROP TABLE IF EXISTS `wx_chat_nickname_check`; CREATE TABLE `wx_chat_nickname_check` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `group_id` int(9) UNSIGNED NOT NULL, `wx_puid` VARCHAR(16) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', `nickname` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create time', PRIMARY KEY `id` (`id`), INDEX `idx_group_id` (`group_id`), INDEX `idx_create_time` (`create_time`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci;
2. 用户设置
所有用户自定义变量存入conf文件里,如群名、临时存储路径、数据库接入信息,踢人阈值:
[wechat] group_name_1=德国IT职业信息分享群 group_id_1=1 path_tmp=/opt/tmp/ notice_random=5 kick_max=10 tuling_api_key=xxxxx [mysql] mysql_host=localhost mysql_port=3306 mysql_user=root mysql_pwd=xxxx mysql_database=wechat_group_ibot
3. 监听群消息
初始化群聊对象,并且监听群消息
# 查找群聊,并且设置附加属性,以备后用
def init_group(group_name, group_id):
group = ensure_one(bot.groups().search(group_name))
group.ext_attr = lambda: None
setattr(group.ext_attr, 'group_id', group_id)
setattr(group.ext_attr, 'group_name', group_name)
return group
# 初始化微信机器人bot
bot = Bot(cache_path=True, console_qr=True)
# unique chat person's id
bot.enable_puid()
# 读取自定义参数
cf = configparser.ConfigParser()
cf.read('wechat.conf')
group_name_1 = cf.get('wechat', 'group_name_1')
group_id_1 = cf.get('wechat', 'group_id_1')
# 初始化群聊对象
group_1 = init_group(group_name_1, group_id_1)
# 监听类型为NOTE的群消息,如:"aa"邀请"bbb"加入了群聊
@bot.register(group_1, NOTE)
def welcome_for_group(msg):
try:
new_member_name = re.search(r'邀请"(.+?)"|"(.+?)"通过', msg.text).group(1)
except AttributeError:
return
group_1.send(welcome_text.format(new_member_name, space_after_chat_at))
# 保持bot持续运行
bot.join()4. 昵称检查
检查群友昵称,存入数据库并且发送提醒, 具体逻辑代码这里不予累述。
def check_nickname(nickname):
# 正则检验群昵称是否标准
if re.match(r'([一-龥]|[ -~]|[sS])+|([一-龥]|[ -~])+|([一-龥]|[ -~])+', nickname):
return True
else:
return False
......
# 检查群友昵称
def process_group_members(group):
# 每次检查前先刷新群成员信息,避免用户改了昵称后再次被提醒
# 但刷新会改变成员临时的内部puid,所以检查昵称必须同时结合puid和nickname
group.update_group(members_details=False)
......
for member in group:
nickname = member.name
wx_puid = member.puid
if not check_nickname(nickname):
invalid_member = GroupMember(nickname, wx_puid, 0)
invalid_members.append(invalid_member)
.....
# 随机抽取不合格的5人
random_members = random.sample(invalid_members, k=5)
......
# 将本次提醒群友存入数据库,供下次计数
def insert_invalid_name(group_id, wx_puid, nickname):
bot_db.execute("INSERT INTO wx_chat_nickname_check (`group_id`, `wx_puid`, `nickname`)"
" VALUES (%s, %s, %s)",
(group_id, wx_puid, nickname))
# 获取昵称不合规群友被提醒计数
def get_invalid_name_count(group_id, wx_puid, nickname):
result = bot_db.get_count("SELECT id FROM wx_chat_nickname_check "
"WHERE group_id = %s and (wx_puid = %s or nickname = %s)", (group_id, wx_puid, nickname))
return result5. 数据库连接池
这里的数据库连接使用了数据库连接池:DBUtils.PersistentDB
DBUtils.PooledDB: 适用于多线程频繁开启关闭数据库连接
DBUtils.PersistentDB:适用于单线程多次频繁连接数据库
如果不采用线程池而是采取直连,那么运行一段时间后,脚本将出现该错误
pymysql.err.OperationalError: 2006
这里将DBUtils再次封装了一下,写了一个单例模式BotDatabase, 提供了query(select), execute(update, delete) 以及批处理execute等常用接口。
6. 启动定时器
# 早八点晚八点各执行检查一次 def start_schedule_for_checking_member(group): scheduler = BlockingScheduler() scheduler.add_job(lambda: process_group_members(group), 'cron', hour=8, minute=1, timezone="Europe/Paris") scheduler.add_job(lambda: process_group_members(group), 'cron', hour=20, minute=1, timezone="Europe/Paris")
最终成果


已知问题
在消息中输入 @群员昵称 并不能真正让该群友收到@提示(显示推送提示),微信App里是在@群员昵称后自动加上了一个特殊的显示空白的字符u’ ′。但是经测试,加上这个符号也不行,推测是微信Web API基于防范垃圾推送,屏蔽了群提示接口。
wxpy的bot在运行一段时间后会停止工作,出现连接服务器错误,必须重新登录,推测是微信Web API的Session安全机制导致的问题。
数据清洗
一段时间后大部分群友修改了昵称,于是有了在德中国程序员职业和专业方向的数据,经清洗后,导出CSV规格如下。

数据分析
该任务所需第三方库如下:
pip3 install pandas pip3 install matplotlib pip3 install jieba pip3 install wordcloud pip3 install seaborn pip3 install palettable