本文仅用于学习用途,转载请注明出处
关键词
python、fiddler抓包、服务器、我在校园、HTTP
0-引言
因学校要求,班委需提醒未完成打卡同学打卡,否则扣除德育分,影响德育成绩。所以为实现班委我在校园的自动化管理,尝试使用python编写一个爬虫程序挂靠在服务器运行,实现每天的自动提醒管理。
经前期搜索,互联网上的我在校园爬虫未涉及班委的自动化管理,且仅仅通过token或者JWSESSION实现登录,时效性不好且操作麻烦。例如github上面的我在校园自动签到程序,通过token值实现签到,但现已经停止维护,且token值登录已经被我在校园登录给禁止。csdn和博客园的部分文章仅仅涉及通过抓包获取JWSESSION实现登录签到,但是有效期不长。
1-总流程图
2.1-抓包解析过程
部分关键信息已经打码
打开fiddler和我在校园小程序,登录,抓取到登录信息如下
由其中发现,我在校园登录是通过url中的符号 ? 来实现传值的,由于登录的时候没有后面的openid等值,猜测传入账号和密码就可以实现登录。
经检验,猜测正确。
按照正常流程进入班级管理界面,依次进入日渐日报管理和班级签到管理,而后检查抓包信息
我在校园班级管理界面
日检日报界面
同理进入签到页面检查,发现出入的参数有一个id
经检查,在上一级页面的respond的中,发现了该天的id,图略
2.2-构建爬虫程序
代码有注释,变量均采用英文命名,可直接阅读
#!/usr/bin/env Python
# coding=utf-8
import requests
import json
import time
class auto_tips:
def __init__(self, username, password):
self.not_heat_names_list = []
self.not_sign_names_list = []
# 保留登录信息
self.session = requests.session()
self.cookies = None
# 登入界面的post信息
self.login_url = 'https://gw.wozaixiaoyuan.com/basicinfo/mobile/login/username'
self.login_data = {
'username': username,
'password': password
}
self.login_headers = {
'Host': 'gw.wozaixiaoyuan.com',
'Connection': 'keep-alive',
'Content-Length': '2',
'Accept': 'application/json, text/plain, */*',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat',
'Content-Type': 'application/json;charset=UTF-8',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-us,en'
}
# 班级日检日报的信息
self.getHeatUsers_url = 'https://student.wozaixiaoyuan.com/heat/getHeatUsers.json'
self.getHeatUsers_headers = {
'Host': 'student.wozaixiaoyuan.com',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat',
'content-type': 'application/x-www-form-urlencoded',
'Accept-Encoding': 'gzip, deflate, br'
}
self.getHeatUsers_data = {'seq': '1', 'date': str(time.strftime("%Y%m%d")), 'type': '0'} # 测试用的,后边程序会更改
# 校区签到的列表
self.getList_url = 'https://student.wozaixiaoyuan.com/gradeManage/sign/getList.json'
self.getList_headers = {
'Host': 'student.wozaixiaoyuan.com',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat',
'content-type': 'application/x-www-form-urlencoded',
'Accept-Encoding': 'gzip, deflate, br'
}
self.getList_date = {
'keyword': '',
'page': '1'
}
# 校区签到的班级成员情况
self.getSignResult_url = 'https://student.wozaixiaoyuan.com/gradeManage/sign/getSignResult.json'
self.getSignResult_headers = {
'Host': 'student.wozaixiaoyuan.com',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat',
'content-type': 'application/x-www-form-urlencoded',
'Accept-Encoding': 'gzip, deflate, br'
}
self.getSignResult_data = {
'id': ''
}
def login(self): # 登入账号
self.login_url = self.login_url + "?username=" + str(self.login_data['username']) + "&password=" + str(
self.login_data['password']) # 通过 url 的 '?' 方法写入参数 # 更新url
res = self.session.post(url=self.login_url, headers=self.login_headers, data='{}')
try:
if json.loads(res.text)['code'] == 0:
self.session.cookies.set('JWSESSION', res.headers['JWSESSION']) # 更新JWSESSION值
print('{}\t登录成功'.format(time.strftime("%H:%M:%S")))
else:
print('{}\t登录失败:{}'.format(time.strftime("%H:%M:%S"), json.loads(res.text)['message'])) # 输出登录错误的错误原因
except:
print('{}\t登录错误'.format(time.strftime("%H:%M:%S")))
def get_HeatUsers(self):
try:
res = self.session.post(url=self.getHeatUsers_url, headers=self.getHeatUsers_headers,
data=self.getHeatUsers_data)
if json.loads(res.text)['code'] == 0: # 检查是否请求成功且正确
# print('{}\t成功获得班级成员日检日报信息'.format(time.strftime("%H:%M:%S")))
data = json.loads(res.text)['data'] # 观察是否有不晨检的同学
if len(data) != 0:
for inf in data: # 如果有,则更新不晨检列表
name = inf['name']
self.not_heat_names_list.append(name)
print('{}\t未完成日检日报的班级成员为{}'.format(time.strftime("%H:%M:%S"), self.not_heat_names_list))
else:
print('{}\t班级成员全部完成日检日报'.format(time.strftime("%H:%M:%S")))
else:
print('{}\t获得班级成员日检日报信息失败:{}'.format(time.strftime("%H:%M:%S"), json.loads(res.text)['message'])) # 输出登录错误的错误原因
except:
print('{}\t获得班级成员日检日报信息失败'.format(time.strftime("%H:%M:%S")))
def get_SignResult(self):
# 获取当天的签到 id 值
try:
res = self.session.post(url=self.getList_url, headers=self.getList_headers,
data=self.getList_date)
if json.loads(res.text)['data'][0]['end'].split(' ')[0] == time.strftime("%Y-%m-%d"): # 判断第一个签到是不是当天的签到
self.getSignResult_data['id'] = json.loads(res.text)['data'][0]['id'] # 如果判断成功,则更新 查询签到的id
res = self.session.post(url=self.getSignResult_url, headers=self.getSignResult_headers, # 查询签到
data=self.getSignResult_data)
data = json.loads(res.text)['data']
if len(data['notSign']) != 0: # 判断是否有成员不签到
for inf in data['notSign']:
name = inf['name']
self.not_sign_names_list.append(name)
print('{}\t未完成签到的班级成员为{}'.format(time.strftime("%H:%M:%S"), self.not_sign_names_list))
else:
print('{}\t班级成员全部完成签到'.format(time.strftime("%H:%M:%S")))
except:
print('{}\t获得班级成员签到信息失败'.format(time.strftime("%H:%M:%S")))
if __name__ == '__main__':
obj = auto_tips('账号名', '密码')
obj.login()
obj.get_HeatUsers()
obj.get_SignResult()
2.3-构建邮件提醒程序
同上,可直接阅读
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
class email_obj:
def __init__(self, to_addr=None, email_Subject=None, email_content=None):
data_inf_path = r'系统日志\email_data.txt' # 邮箱信息的存储路径
with open(data_inf_path, 'r+', encoding='UTF-8') as f:
data_inf = eval(f.read()) # 读取邮箱信息
f.close()
self.from_addr = data_inf['发信邮箱'] # 发信邮箱
self.password = data_inf['邮箱授权码'] # 发信邮箱的授权码
self.smtp_server = data_inf['发信服务器'] # 发信服务器的域名
self.to_addr = to_addr # 收信邮箱
self.email_Subject = email_Subject # 邮件标题
self.email_content = email_content # 邮件内容
# 开启发信服务,这里使用的是加密传输
self.server = smtplib.SMTP_SSL(host=self.smtp_server) # POP3/SMTP 协议的发送邮件服务器
self.server.connect(self.smtp_server, 465) # 使用SSL,端口号465
# 登录发信邮箱
self.server.login(self.from_addr, self.password)
def change_email_inf_to(self, to_addr, email_Subject, email_content):
self.to_addr = to_addr # 收信邮箱
self.email_Subject = email_Subject # 邮件标题
self.email_content = email_content # 邮件内容
def send(self):
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
msg = MIMEText(self.email_content, 'plain', 'utf-8')
# 邮件头信息
msg['From'] = Header(self.from_addr)
msg['To'] = Header(self.to_addr)
msg['Subject'] = Header(self.email_Subject)
# 发送邮件
self.server.sendmail(self.from_addr, self.to_addr, msg.as_string())
def close(self):
# 关闭服务器
self.server.quit()
if __name__ == '__main__':
to_addr = 'xxx@xx.xxx'
email_Subject = 'send by python'
email_content = 'python test'
obj = email_obj(to_addr, email_Subject, email_content)
obj.send()
定时触发程序
#!/usr/bin/env Python
# coding=utf-8
import tips
import send_email
import pandas as pd
import time
def heat_tip(file): # 晨检提醒
obj = tips.auto_tips('真实账号', '真实密码')
obj.login()
obj.get_HeatUsers()
name_list = obj.not_heat_names_list
if len(name_list) != 0:
user_email = send_email.email_obj() # 登录邮箱
for i in range(len(name_list)): # 依次编辑文件
name_i = name_list[i]
for j in range(len(file)):
name_j = file.iloc[j, 0]
if name_i == name_j:
qq = file.iloc[j, 1]
qq_email = qq + '@qq.com'
email_Subject = '日检日报提醒'
email_content = '赶紧晨检了,现在已经是{}了'.format(time.strftime("%H:%M:%S"))
user_email.change_email_inf_to(to_addr=qq_email, email_Subject=email_Subject,
email_content=email_content)
user_email.send()
user_email.close() # 退出邮箱
def sign_tip(file): # 签到提醒
obj = tips.auto_tips('真实账号', '真实密码')
obj.login()
obj.get_SignResult()
name_list = obj.not_sign_names_list
if len(name_list) != 0:
user_email = send_email.email_obj() # 登录邮箱
for i in range(len(name_list)): # 依次编辑文件
name_i = name_list[i]
for j in range(len(file)):
name_j = file.iloc[j, 0]
if name_i == name_j:
qq = file.iloc[j, 1]
qq_email = qq + '@qq.com'
email_Subject = '签到提醒'
email_content = '赶紧晨检了,现在已经是{}了'.format(time.strftime("%H:%M:%S"))
user_email.change_email_inf_to(to_addr=qq_email, email_Subject=email_Subject,
email_content=email_content)
user_email.send()
user_email.close() # 退出邮箱
def seconds(now_time): # 把时间转化为秒
hour = int(now_time.split(':')[0])
minute = int(now_time.split(':')[1])
second = int(now_time.split(':')[2])
now_seconds = hour * 60 * 60 + minute * 60 + second
return now_seconds
def get_sleep_time(now_time, to_time): # 获得程序睡眠时间
sleep_time = seconds(to_time) - seconds(now_time)
return sleep_time # 单位是s
def auto_tips():
file = pd.read_excel(r'用户信息\班级成员信息.xlsx')
print('开始运行')
# 晨检提醒时间
heat_tip_time_1 = '11:00:00'
heat_tip_time_2 = '11:30:00'
heat_tip_time_3 = '11:50:00'
# 签到提醒时间
sign_tip_time_1 = '22:05:00'
sign_tip_time_2 = '22:15:00'
sign_tip_time_3 = '22:25:00'
tips_time_list = [heat_tip_time_1, heat_tip_time_2, heat_tip_time_3, sign_tip_time_1, sign_tip_time_2,
sign_tip_time_3] # 按顺序放好
####
while True:
flag = 0 # 判断当前时间是否在提醒时间前
tip_name = None # 提醒的事件名称
sleep_time = None # 睡眠时间
for i in range(len(tips_time_list)): # 判断当前时间
tip_time = tips_time_list[i]
now_time = time.strftime("%H:%M:%S") # 时:分:秒
if seconds(now_time) < seconds(tip_time):
sleep_time = get_sleep_time(now_time=now_time, to_time=tip_time)
if i <= 2:
tip_name = '晨检'
else:
tip_name = '签到'
flag = 1
break
if flag == 0:
now_time = time.strftime("%H:%M:%S") # 时:分:秒
sleep_time = get_sleep_time(now_time=now_time, to_time='24:00:00') + get_sleep_time(now_time='00:00:00',
to_time=tips_time_list[0])
tip_name = '晨检'
print('{}\t即将休眠{}秒'.format(time.strftime("%H:%M:%S"), sleep_time))
time.sleep(sleep_time)
if tip_name == '晨检':
heat_tip(file)
elif tip_name == '签到':
sign_tip(file)
print('{}\t提醒信息已经发送'.format(time.strftime("%H:%M:%S")))
if __name__ == '__main__':
# print(seconds('07:10:12'))
auto_tips()
2.4-配置文件
文件如图配置
2.5-打包并且配置到服务器上
利用pyinstaller打包auto_tips.py
然后用命令控制符的mstsc命令,连接远程服务器,把整个文件夹上传到服务器运行。
3-过程碰到的问题
3.1-抓取不到登录界面
问题描述打开微信小程序,进入我在校园后,只获得了get信息,然后就自动转接微信授权登录,无法抓取到post信息
解决方法换另一台电脑登录后,再重新返回本电脑登录我在校园,微信授权登录就会失败,然后就可以账号密码登入,抓取到对应的信息
3.2-获取不了邮箱的授权码
问题描述我用的是阿里云购买的域名,解析到腾讯企业邮箱的服务器上,注册了域名邮箱。找不到对应的授权码
解决方法打开腾讯企业邮箱,给对应的邮箱开头SMTP服务,然后进入邮箱,设置中处理就可以了。详细教程网上可以查到
3.3nssm-把exe文件注册成服务
问题描述把exe文件通过nssm程序注册成计算机服务,后台运行服务时候,运行失败
暂未解决