使用python实现自动化打卡


脚本编写过程记录

本来我是不必造轮子来写这个自动化打卡脚本的,但是无奈之前依赖的脚本在一次删库事件中丢失了,然后在某人的push之下完成了该脚本,感谢伟大的python以及python各类库的开发者们,你们真滴很不错~

登录过程

在进行写脚本之前首先跑通整个登录过程并抓包分析,本文是根据同院同学的打卡脚本找到的登录入口,现在学校已经进行了微服务的升级,升级之后的登录入口更改,并且需要验证码校验,等有空了再研究。

登录入口如下,https://wfw.scu.edu.cn/site/polymerization/polymerizationLogin?redirect=https%3A%2F%2Fwfw.scu.edu.cn%2Fncov%2Fwap%2Fdefault%2Findex&from=wap

点击进入该链接后使用统一账户登录,此时将会发送一POST请求,内容如下:

image-20211223145309586

登录完成后获取到cookie,同时跳转至https://wfw.scu.edu.cn/ncov/wap/default/index,可以获取到上次填写的信息

image-20211223145519568

最后在https://wfw.scu.edu.cn/ncov/wap/default/save提交填报信息

image-20211224100451717

基础知识

HTTP头字段(英语:HTTP header fields)是指在超文本传输协议(HTTP)的请求和响应消息中的消息头部分。它们定义了一个超文本传输协议事务中的操作参数。HTTP头部字段可以自己根据需要定义,因此可能在 Web 服务器和浏览器上发现非标准的头字段

头部字段代表含义,可在https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers中查询

登录请求

使用pythonrequests库发送请求

HTTP协议本身是无状态的,为了让请求之间保持状态,有了sessioncookie机制。requests也提供了相应的方法去操纵它们。

requests中的session对象能够让我们跨HTTP请求保持某些参数,即在同一个 Session 实例发出的所有请求之间保持 cookie

在请求之前,首先声明一个新的session用于会话保持

import requests
session=requests.Session()
r=session.get(url,header)#后续请求发送示意

接下来进行账户登录,设置好post传输的data以及headers,虽然使用的是dict格式,但Reqeusts支持自动将传递给requests.post()data参数以form表单形式发送post请求,因此使用的数据提交格式仍然为application/x-www-form-urlencoded

loginURL='https://wfw.scu.edu.cn/a_scu/api/sso/check'
loginData={
    'username': username,
    'password': password
}
loginHeader={
    'Host':'wfw.scu.edu.cn',
    'User-Agent':'',
    'Origin':'https://wfw.scu.edu.cn',
    'Referer':''
}

发送登录请求,session对象会帮助我们保存cookie,说白了我们登录的原因也就是为了获取cookie

loginR=session.post(loginURL,data=loginData,headers=loginHeader)

获取信息

现在已获取了cookie,可以使用session对象拉取上次填报信息,同样好设置请求头,伪装成一次正常的访问,提前在burp中抓包可以预想到接收到的内容

image-20211223205502727

接收到的内容比较复杂,但是我们记得在平时打卡的时候,大部分时候我们都不需要进行一项一项地填写,系统将会为我们自动填充上次填写的内容,因此我们可以在返回的response中找到我们上次填写的信息oldInfo

我们直接将response切换为string格式,然后使用正则匹配式找到我们上次填写的oldInfo信息,再把字符串转为dict格式,方便post请求上传data

import requests
import re
import json
getR=session.get(getURL,headers=getHeader).text
oldInfo=re.findall(r'.*?oldInfo: (.*),.*?',getR)#正则匹配
oldData=eval(oldInfo[0])#将字符转化为json

re.findall (返回string中所有与pattern相匹配的全部字串,返回形式为数组),使用方法如下

re.findall(pattern, string, flags=0)

提交信息

将刚才获取的oldInfo使用POST请求发送到目标URL

postURL='https://wfw.scu.edu.cn/ncov/wap/default/save'
postHeader={
    'Host':'wfw.scu.edu.cn',
    'User-Agent':'',
    'Content-Type': 'application/x-www-form-urlencoded;',
    'X-Requested-With': 'XMLHttpRequest',
    'Content-Length': '2082',
    'Origin':'',
    'Connection': 'keep-alive',
    'Referer':''
}
postR=session.post(postURL,data=oldData,headers=postHeader).json()

配置邮件客户端

SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。

Python对SMTP支持有smtplibemail两个模块,email负责构造邮件,smtplib负责发送邮件。

其实现在已经填报成功了,不过还是设个邮件每天播报一下,说不定哪一天就gg了呢

使用到的库如下

import smtplib
from email.mime.text import MIMEText

构造MIMEText对象时,第一个参数就是邮件正文,第二个参数是MIME的subtype,传入plain表示纯文本,最终的MIME就是'text/plain',最后一定要用utf-8编码保证多语言兼容性。

msg = MIMEText(content, 'plain', 'utf-8')
msg['Subject'] = '打卡播报'
msg['From'] = From
msg['To'] = To

发送邮件的过程如下,

client = smtplib.SMTP_SSL('smtp.qq.com', smtplib.SMTP_SSL_PORT)#设计邮件客户端
client.login(From, pwd)#登录邮件服务器
client.sendmail(From, To, msg.as_string())
client.quit()

login()方法用来登录SMTP服务器,sendmail()方法就是发邮件,邮件正文是一个stras_string()MIMEText对象变成str

这里的mail_password填写的为授权码而非自己的QQ邮箱密码,获取授权码的过程如下

image-20211222094423561

image-20211222094518751

定时任务

设定为每天早上执行任务,在文章[1]中总结了几种定时执行任务的方法,本文使用的是任务框架APScheduler

使用的库以及使用方法如下:

from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.schedulers.background import BackgroundScheduler
#创建调度器:BlockingScheduler
scheduler = BlockingScheduler(timezone='Asia/Shanghai')
#添加任务
scheduler.add_job(funcName, 'cron',hour=7,minute=40)
scheduler.start()

时区设置不是必须的,如果你能忍受长段warning的话

另外APScheduler支持多种调度方法,包括了date(一次性日期),interval(间隔)和cron(个性化)

# 2016-12-12 12:00:00运行一次job_function
sched.add_job(job_function, 'date', run_date=datetime(2016, 12, 12, 12, 0, 0), args=['text'])
# 每两个小时调一下job_function
sched.add_job(job_function, 'interval', hours=2)
# 截止到2016-12-30 00:00:00,每周一到周五早上五点半运行job_function
sched.add_job(job_function, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2016-12-31')

总结

感谢同院同学的脚本参考,让我在实施过程中少走了很多弯路,同时再一次认识到自己的不足之处,对于HTTP请求头以及正则表达式的理解不够充分,下次另外写文章专门整理一下。


文章作者: Janea1
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Janea1 !
评论
  目录