前言

本工具仅供学习使用,大家注意遵守学校规章制度.如有身体不适及时上报.

使用selenium完成健康打卡
一年前,写了selenium打卡的代码.一年来也有不断地更新版本,添加邮件通知功能,http get请求进行腾讯文档标注(后来腾讯文档改版,隐藏了接口).以及用post登入验证打卡是否成功.到今天,算是完成了2.0版本的,现在整个程序觉得还蛮满意的(强迫症的满足).


一、为什么想改?

1. 强迫症

虽然selenium已经很方便的,但我还是觉得不够优雅,使用浏览器+driver的方式太臃肿了,相比于图形操作的方式,我更喜欢命令行的方式.

2. 不方便

有不少同学会想找我要代码,但是我想介绍的时候,即使是计算机专业的同学介绍还是蛮麻烦的,毕竟需要配置driver环境变量,还要下驱动.非计算机专业的同学大概率没得python环境就更难介绍了.(后续查好像selenium也可以打包,不确定没装驱动可不可以)

3. 不稳定

其实用selenium我个人觉得不稳定因素贼多,老是会出错,所以我会在一年里用了很多方法处理,比如邮件确定,try catch捕获异常,用post登入确保一定登上.这一系列的操作,让程序又臃肿起来了.
第二个不稳定因素是webdriver的问题,Chrome老自动更新.driver也要更新挺烦的

二 、过程

1. 登入系统

抓包登入接口.此处有一个问题需要处理:
登入的密码其实与我们的密码不一致,是经过算法加密的.所以想要直接使用这个接口的话,需要对代码进行处理.我找了好哥们儿帮我找到了加密方式,解决了这个问题.其实也有替代方案,要哪个账号打卡就登入一下抓包就能拿到了,不过不优雅.
这个接口能得到的响应:

这里的响应会有msg,用于判断是否打卡
jnuid后续post的重要凭证

2.获取历史信息

拿到jnuid以后就可以通过这个另外一个接口获取信息

3.用历史信息构造请求体数据,post

写的时候发现,暴露的东西有点点多,我还是把具体步骤隐藏起来吧,下面的代码是可以用的,但是需要自己抓包接口.另外我还发现了一个问题,觉得系统不是很安全.我还是不要暴露了

然后发现打卡并没有用session.如果有的话其实更方便,用session.request()


三 、使用(不发邮件版)

1.我将代码打包成exe文件(可直接在Windows10运行).这里我修改了代码为本地版本的,不再发邮件而是使用log.txt的形式进行记录.使用的打包工具是pyinstaller我这里打包的是带命令行界面版的
2.以account.txt作为账户输入.格式为账户 (空格) 密码 (空格) 姓名
如果多个账户换行即可.示例:

20xxxxx xxxxx 张三
20xxxxx xxxxx 李四

相应的打卡记录会显示在同一目录下的log.txt中
3. Windows定时任务定时执行代码.(可以看后面的参考资料)


四、代码(未阉割版)

以下为批量打卡程序

import post_daka_success
from email.mime.text import MIMEText
import smtplib
account = [['账号','密码','姓名','邮箱']]

msg_from = ''  # 发送方邮箱
passwd = ''  # 填入发送方邮箱的授权码
msg_to = ['']  # 收件人邮箱
subject = "打卡邮件"  # 主题
content = ""

for acc in account:
    obj = post_daka_success.daka(acc[0],acc[1])
    obj.run()
    if obj.check():
        content += "程序已经为"+acc[2]+"打卡\n"
    else:
        content += acc[2]+"未打卡\n"


s = smtplib.SMTP_SSL("smtp.qq.com", 465)
msg = MIMEText(content)
msg['Subject'] = subject
msg['From'] = msg_from
msg['To'] = account[0][3]
s.login(msg_from, passwd)
s.sendmail(msg_from, account[0][3], msg.as_string())

以下为打卡用的程序

# -*- coding:utf-8 -*-
import requests
import json
import base64
from Crypto.Cipher import AES
import time
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.hazmat.primitives import padding


class daka:
    headers = {"accept": "application/json, text/plain, */*",
               "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
               "content-type": "application/json",
               "sec-fetch-dest": "empty",
               "sec-fetch-mode": "cors",
               "sec-fetch-site": "same-origin",
               "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36"}
    sucess = "登录成功,今天已填写"

    def __init__(self, account, password):
        super().__init__()
        self.account = account
        self.password = password
        self.body = {"username": account,
                     "password": self.jiami(password)}
        self.body = json.dumps(self.body)

    def pkcs7_padding(self, data):
        padder = padding.PKCS7(algorithms.AES.block_size).padder()
        padded_data = padder.update(data.encode('utf-8'))+padder.finalize()
        return padded_data

    # 将密码转为加密后的代码
    def jiami(self, data):
        key = 'xAt9Ye&SouxCJziN'.encode('utf-8')
        cryptor = AES.new(key, AES.MODE_CBC, key)
        res = base64.b64encode(cryptor.encrypt(self.pkcs7_padding(
            data))).decode().replace('/', '_').replace('+', '-')
        res = list(res)
        res[-2] = "*"
        res = "".join(res)
        return res

    # 登入
    def login(self):
        url = ''#登入的网址
        res_login = requests.post(
            url, data=self.body, headers=self.headers)
        res_login_data = json.loads(res_login.text)
        print(res_login_data)
        self.res_login_data = res_login_data

    # 获取打卡需要的相关信息
    def get_info_data(self):
        res_login_data = self.res_login_data
        get_info_data = {}
        get_info_data['jnuid'] = res_login_data['data']['jnuid']
        get_info_data['idType'] = '1'
        # print(get_info_data)
        get_info_data = json.dumps(get_info_data)
        info_url = ''#获取信息的网址
        res_info_data = requests.post(
            info_url, data=str(get_info_data).encode('utf-8'), headers=self.headers)
        res_info_data = json.loads(res_info_data.text)
        self.res_info_data = res_info_data
        # print(res_info_data)

    def post(self):
        res_info_data = self.res_info_data
        daka_post_url = ""#post的网址
        post_data = {}
        post_data['mainTable'] = {}#这里需要抓包填一下学校的地址
        post_data['mainTable']['leaveTransportationOther'] = res_info_data['data']['mainTable']['leaveTransportationOther']
        post_data['mainTable']['way2Start'] = res_info_data['data']['mainTable']['way2Start']
        post_data['mainTable']['language'] = res_info_data['data']['mainTable']['language']
        post_data['mainTable']['declareTime'] = res_info_data['data']['declare_time']
        post_data['mainTable']['personNo'] = res_info_data['data']['jnuId']
        post_data['mainTable']['personName'] = res_info_data['data']['xm']
        post_data['mainTable']['sex'] = res_info_data['data']['xbm']
        post_data['mainTable']['professionName'] = res_info_data['data']['zy']
        post_data['mainTable']['collegeName'] = res_info_data['data']['yxsmc']
        post_data['mainTable']['phoneArea'] = res_info_data['data']['mainTable']['phoneArea']
        post_data['mainTable']['phone'] = res_info_data['data']['mainTable']['phone']
        post_data['mainTable']['assistantName'] = res_info_data['data']['mainTable']['assistantName']
        post_data['mainTable']['assistantNo'] = res_info_data['data']['mainTable']['assistantNo']
        post_data['mainTable']['className'] = res_info_data['data']['mainTable']['className']
        post_data['mainTable']['linkman'] = res_info_data['data']['mainTable']['linkman']
        post_data['mainTable']['linkmanPhoneArea'] = res_info_data['data']['mainTable']['linkmanPhoneArea']
        post_data['mainTable']['linkmanPhone'] = res_info_data['data']['mainTable']['linkmanPhone']
        post_data['mainTable']['personHealth'] = res_info_data['data']['mainTable']['personHealth']
        post_data['mainTable']['temperature'] = res_info_data['data']['mainTable']['temperature']
        post_data['mainTable']['personHealth2'] = res_info_data['data']['mainTable']['personHealth2']
        post_data['mainTable']['schoolC1'] = res_info_data['data']['mainTable']['schoolC1']
        post_data['mainTable']['currentArea'] = res_info_data['data']['mainTable']['currentArea']
        post_data['mainTable']['personC4'] = res_info_data['data']['mainTable']['personC4']
        post_data['mainTable']['otherC4'] = res_info_data['data']['mainTable']['otherC4']
        post_data['mainTable']['isPass14C1'] = res_info_data['data']['mainTable']['isPass14C1']
        post_data['mainTable']['isPass14C2'] = res_info_data['data']['mainTable']['isPass14C2']
        post_data['mainTable']['isPass14C3'] = res_info_data['data']['mainTable']['isPass14C3']
        post_data['secondTable'] = {}
        post_data['secondTable']['other1'] = res_info_data['data']['secondTable']['other1']
        post_data['secondTable']['other3'] = res_info_data['data']['secondTable']['other3']
        post_data['secondTable']['other4'] = res_info_data['data']['secondTable']['other4']
        post_data['secondTable']['other5'] = res_info_data['data']['secondTable']['other5']
        post_data['secondTable']['other6'] = res_info_data['data']['secondTable']['other6']
        post_data['secondTable']['other7'] = res_info_data['data']['secondTable']['other7']
        post_data['secondTable']['other8'] = res_info_data['data']['secondTable']['other8']
        post_data['secondTable']['other9'] = res_info_data['data']['secondTable']['other9']
        post_data['jnuid'] = self.res_login_data['data']['jnuid']
        post_data_res = requests.post(
            daka_post_url, data=json.dumps(post_data), headers=self.headers)
        print(post_data_res.text)
        self.post_data_res = json.loads(post_data_res.text)
        
    #检查是否打卡成功
    def check(self):
        self.login()
        if(self.res_login_data['meta']['msg'] == self.sucess):
            print(self.res_login_data['data']['name']+'>>>>'+self.sucess)
            return True
        return False

    def run(self):
        self.login()
        self.get_info_data()
        self.post()


if __name__ == '__main__':
    obj = daka("账户", "密码")
    obj.run()
    obj.yanzhen()

五、参考资料

  1. Windows定时任务
  2. Python PyInstaller安装和使用教程(详解版)
本文版权归去快排Seo www.SEOgurublog.com 所有,如有转发请注明来出,竞价开户托管,seo优化请联系QQ▷61910465