每日打卡签到任务的自动化处理

举报
邓大帅 发表于 2022/05/21 03:22:59 2022/05/21
【摘要】 文章主要介绍了如何根据数据特征直接封装有效的post包发至目标服务器,实现信息报送。

为积极响应信息报送工作,这几天有很多小伙伴来问我能不能实现每日信息报送任务的自动化处理,避免每日信息的漏报、延报。因此抽时间用 Java 写了一份,但是考虑到大家可能更加熟悉 Python,因此重写了一份 Python版本,有条件的童鞋可以尝试。

温馨提示:请对每日报送信息内容的真实性和完整性负责,如有信息变更请及时停止自动化报送,请大家遵守各项规定,配合并听从各项措施和要求。

打卡步骤梳理:
  1. 登录账号
  2. 获取定位
  3. 点击“报送”
  4. 收到服务器“提交信息成功”状态响应

基于上述分析,实现每日信息情况的自动报送思路有二,其一直接用 selenium 模拟浏览器操作实现动态HTML处理,完成报送(不推荐),实现方法较为简单,随手一打,未测试,仅供参考。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import TimeoutException, WebDriverException
from time import sleep
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
zhanghao = ""
mima = ""
chrome_options = Options()
driver = webdriver.Chrome()
browser = webdriver.Chrome(chrome_options=chrome_options)
browser.get('https://app.cupl.edu.cn/ncov/wap/default/index')
wait = WebDriverWait(browser,10,0.5)
account = WebDriverWait(browser, 20).until(expected_conditions.visibility_of_element_located((By.XPATH, '//*[@id="app"]/div[2]/div[1]/input')))\
    .send_keys(zhanghao)
password = browser.find_element_by_xpath('//*[@id="app"]/div[2]/div[2]/input').send_keys(mima)
sign_in = browser.find_element_by_class_name('btn').click()
wait.until(EC.visibility_of_element_located((By.XPATH,'/html/body/div[1]/div/div/section/div[4]/ul/li[7]/div/input'))).click()
sleep(3)
browser.find_element_by_xpath('/html/body/div[1]/div/div/section/div[5]/div/a').click()
sleep(1)
browser.find_element_by_xpath('//*[@id="wapcf"]/div/div[2]/div[2]').click()
sleep(1)
browser.close()
browser.quit()

上述方法实现起来很简单,勉强算个”半自动化“,但弊端很明显:

  • 其一,上述方法需要chromedriver.exe等环境依赖,体积大,运行慢,不便封装;
  • 其二,需要明文存储账户信息,安全性不可靠;
  • 其三,每次报送需定位,过程慢,容错率低,且不支持异地主机自动化处理;
  • 其四,除非每日手动开启,不易监听报送结果。

相比,第二个实现方案能够很好解决上述问题: 不再依赖可视化的前端操作,根据数据特征直接封装有效的post包发至目标服务器,实现信息报送,并监听服务器响应,通过邮件接受报送结果。

(图示邮件为部署完成后”每日定时自动打卡执行情况“监听结果)
在这里插入图片描述
在这里插入图片描述

实现方法如下:


一、抓包与析包
(1)登陆账号验证cookie值有效期

在这里插入图片描述
在这里插入图片描述

(2)定位post请求与header头

在这里插入图片描述
在这里插入图片描述

(3)解析post包报文内容

在这里插入图片描述
经过报文分析,确定变量有3,分别是 createddate 以及 id
在这里插入图片描述
其中date 容易理解,进一步回到前端分析 createdid

在这里插入图片描述

分析后认为,created_uid 与 created 具有函数关系,id 应该是根据时间戳产生,id 与 created 每日变化且相互关联,但一组 id 与 created 不可复用。

同时我发现,每次页面载入 GET 数据库信息后将通过声明 def变量的形式体现在源码中。

基于上述分析,直接尝试组装数据包并发送 ————> 提交成功。

在这里插入图片描述


二、实现信息报送的自动化

在明白原理后,原本复杂的问题就已经迎刃而解了。先梳理清楚操作步骤和目标,并引入相关环境变量:

  1. 获取 createddate 以及 id 的值;
  2. 拼接 post 包
  3. 发包
  4. 获取服务器状态响应
  5. 每日邮件提醒响应内容
  6. 定时任务,每日自动运行
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
import urllib.request
import urllib.parse
import re
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
(1)获取 createddate 以及 id 的值

前面我们已经发现,每次页面载入 GET 数据库信息后将通过声明 def变量的形式体现在源码中,那就直接用正则表达式截取内容:

首先,createddate 以及 id 的值只有在登陆后才能获取,因此获取网页源码时需要带上 Cookie 值和必要的请求头。

url = 'https://app.cupl.edu.cn/ncov/wap/default/index'

headers = {
    'GET https': '//app.cupl.edu.cn/ncov/wap/default/index HTTP/1.1',
    'Host': ' app.cupl.edu.cn',
    'Connection': ' keep-alive',
    'Upgrade-Insecure-Requests': ' 1',
    'User-Agent': ' Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
    'Accept': ' text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Referer':' https://app.cupl.edu.cn/ncov/wap/default/index',
    'Accept-Language': ' zh-CN,zh;q=0.9',
    'Cookie': 'Hm_lvt_48b682d4885d22a90111e46b...',
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
main = response.read().decode(encoding='UTF-8',errors='strict')

其次,上正则表达式,注意creatediddate 间存在格式差异!!!

reg_id = r'"id":[\d]+'
reg_created = r'"created":[\d]+'
reg_date = r'"date":"[\d]+"'

reg_ques_id = re.compile(reg_id)
reg_ques_created = re.compile(reg_created)
reg_ques_date = re.compile(reg_date)

queslist_id = reg_ques_id.findall(main)
queslist_created = reg_ques_created.findall(main)
queslist_date = reg_ques_date.findall(main)

分别看一下返回结果:

#print(queslist_id)
#print(queslist_created)
#print(queslist_date)

在这里插入图片描述
我们可以看到,返回的数据类型是 <class ‘list’>,提取createddate 以及 id 的值,首先要将列表转化成 str,再行截取。

new_id = str(queslist_id[0])[5:11]
#print(new_id)

new_created = str(queslist_created[0])[10:20]
#print(new_created)

new_date = str(queslist_date[0])[8:16]
#print(new_date)

在这里插入图片描述

(2)拼接 post 包、发包、收包
new_body = "ismoved=0&j······&created="+new_created+"&date="+new_date+"&id="+new_id+"&gwszdd=&sfyqjzgc=&jcqzrq=&sfjcqz=&jrsfqzys=&jrsfqzfy=&sfsqhzjkk=&sqhzjkkys="
headers = {
    "Host": "app.cupl.edu.cn",
    "Connection": "keep-alive",
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Origin": "https://app.cupl.edu.cn",
    "X-Requested-With": "XMLHttpRequest",
    "Sec-Fetch-Dest": "empty",
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    "Sec-Fetch-Site": "same-origin",
    "Sec-Fetch-Mode": "cors",
    "Referer": "https://app.cupl.edu.cn/ncov/wap/default/index",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Cookie": "Hm_l······47797"
}
data = new_body
url2 = 'https://app.cupl.edu.cn/ncov/wap/default/save'
resp = requests.post(url2, headers=headers,data=data, allow_redirects=True)
# 获取服务器状态响应
print (resp.text)

在这里插入图片描述

这里回显内容分别为creatediddate 的值;拼接后完整的body; 以及服务器响应状态,可以看到信息已提交成功。

在这里插入图片描述

(3)报送情况监听

全自动化的信息报送是否每天都有正常运行是自动化报送中需要关注的问题,这里选择通过简单邮件传输协议方式监听报送结果。

my_sender = '···'  # 发件人邮箱账号
my_pass = '···'  # 发件人邮箱密码
my_user = '···'  # 收件人邮箱账号,我这边发送给自己

def mail():
    ret = True
    try:
        msg = MIMEText('每日信息报送情况:'+resp.text, 'plain', 'utf-8')
        msg['From'] = formataddr(["···", my_sender])  # 括号里的对应发件人邮箱昵称、发件人邮箱账号
        msg['To'] = formataddr(["···", my_user])  # 括号里的对应收件人邮箱昵称、收件人邮箱账号
        msg['Subject'] = "每日信息报送情况:"+resp.text  # 邮件的主题,也可以说是标题

        server = smtplib.SMTP_SSL("smtp.qq.com", 465)  # 发件人邮箱中的SMTP服务器,端口是25
        server.login(my_sender, my_pass)  # 括号中对应的是发件人邮箱账号、邮箱密码
        server.sendmail(my_sender, [my_user, ], msg.as_string())  # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件
        server.quit()  # 关闭连接
    except Exception:  # 如果 try 中的语句没有执行,则会执行下面的 ret=False
        ret = False
    return ret

ret = mail()
if ret:
    print("邮件发送成功")
else:
    print("邮件发送失败")

模拟测试错误态:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(4)设置定时任务,每日自动执行

考虑到计算机开机的不定期性以及局域网小型服务器面临的断电风险,我选择将程序放置我自己的公网服务器上运行,这里以Linux服务器定时任务为例:

  • 检查服务器时间是否同步:
date -R

在这里插入图片描述

# 使用crontab新建定时任务:
crontab -e
usage:  crontab [-u user] file
        crontab [-u user] [ -e | -l | -r ]
                (default operation is replace, per 1003.2)
        -e      (edit user's crontab)
        -l      (list user's crontab)
        -r      (delete user's crontab)
        -i      (prompt before deleting user's crontab)
        -s      (selinux context)

在这里插入图片描述
设置每天00:01分自动签到,> /dev/null 2>&1目的在于拒收程序指定时间执行后系统自动寄送的信件。

* * * * * /root/rats.sh   #前5*指时间,后面为命令

分钟:0-59
小时:1-23
日期:1-31
月份:1-12
星期:0-60表示周日)

*: 表示任何时刻
,: 表示分割
-:表示一个段,如:1-5,就表示15/n : 表示每个n的单位执行一次,如:*/1, 就表示每隔1个小时执行一次命令。也可以写成1-23/1
1.注意环境变量问题,例如crontab不能识别Java的环境变量,所以在编写shell时,最好使用export重新声明变量,确保脚本执行。 
2.命令的执行最好用脚本
3.脚本权限加/bin/sh,规范路径/server/scripts
4.时间变量用反斜线转义,最好用脚本
5.定时任务添加注释
6.>/dev/null 2>&1   ==>&>/dev/null,别随意打印日志文件
7.定时任务里面的程序脚本尽量用全路径
8.避免不必要的程序以及命令输出
9.定时任务之前添加注释
10.打包到文件目录的上一级
#定时任务设置举例:
43 21 * * *   #21:43 执行
15 05 * * *   #05:15 执行
0 17 * * *    #17:00 执行
0 17 * * 1    #每周一的17:00 执行
0,10 17 * * 0,2,3    #每周日,周二,周三的17:0017:10 执行
0-10 17 1 * *    #毎月1日从17:007:10毎隔1分钟 执行
0 0 1,15 * 1    #毎月1日和15日和一日的0:00 执行
42 4 1 * *   #毎月1日的4:42分 执行
0 21 * * 1-6   #周一到周六21:00 执行
0,10,20,30,40,50 * * * *   #每隔10分 执行
*/10 * * * *   #每隔10分 执行
* 1 * * *   #从1:01:59每隔1分钟 执行
0 1 * * *   #1:00 执行
0 */1 * * *   #毎时0分每隔1小时 执行
0 * * * *   #毎时0分 执行
2 8-20/3 * * *   #8:02,11:02,14:02,17:02,20:02 执行
30 5 1,15 * *   #1日和15日的5:30 执行

在这里插入图片描述
经测试,运行正常:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


再次提示:请对每日报送信息内容的真实性和完整性负责,如有信息变更请及时停止自动化报送,请大家遵守各项规定,配合并听从各项措施和要求。


如果您有任何疑问或者好的建议,期待你的留言与评论!

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200