先附上打卡信息提交网址

https://yiban.raichu.top
仅适用于湖南工程学院健康打卡系统

背景

疫情期间,根据学校要求,每天必须进行健康打卡,在保证自己身体是健康的情况下,和室友一起写了这个脚本,实现每天定时自动打卡

语言及环境

服务器系统:centos7.7
Python版本:3.8.2
Chrome版本:80.0.3987.162
ChromeDriver版本:80.0.3987.16
selenium版本:3.141.0

具体功能

(一)用户信息提交
(二)验证账号正确性
(三)用户查重,如果已经提交过则给出提示且不再提交
(四)自动打卡功能

Python脚本代码(自动打卡部分)

import os
import shutil
import json
import urllib.request
import datetime
from selenium import webdriver
import time
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor
import threading

userfile = "xxxxxxxxxxxxxxxxxxxx"    #原用户文件地址
tempfile = "xxxxxxxxxxxxxx"    #副本文件地址
logsfolder = "xxxxxxxxxxxxxxxxxxxxx"  #log文件夹地址,需要自己创建
templogfile = logsfolder + "/templog.txt"   #不要改动
picfolder = "" #不要改动
n = 10   #重复执行上限,至少为1
count = 0
picfileflag = 1

def deluser(_id,_path):
    with open(_path,'r',encoding = 'UTF-8') as r:
        lines=r.readlines()
        r.close()
    with open(_path,'w',encoding = 'UTF-8') as w:
        for l in lines:
            if _id not in l:
                w.write(l)
        w.close()

def wirtelog(log,_path):
    print(log)
    with open(_path,"a+",encoding = "UTF-8") as f:
        f.write(log)
        f.write('\n')
        f.close()

def piclog(driver,_path,picname):
    picpath = _path + "/" + picname + ".png"
    driver.get_screenshot_as_file(picpath)

def picfile(_path):
    global picfolder
    file = _path + "/" +str(time.strftime("%Y-%m-%d" , time.localtime()))
    picfolder = file
    folder = os.path.exists(file)
    if not folder:
        os.makedirs(file)
    else:
        shutil.rmtree(file)
        os.makedirs(file)

def run(_name,_id,_password):
    global count
    global picfileflag
    try: 
        chromeOptions = webdriver.ChromeOptions()
        chromeOptions.add_argument('--headless')  #浏览器无窗口加载
        chromeOptions.add_argument('--disable-gpu')  #不开启GPU加速
        chromeOptions.add_argument('--disable-dev-shm-usage') 
        chromeOptions.add_argument('--no-sandbox')#以根用户打身份运行Chrome,使用-no-sandbox标记重新运行Chrome,禁止沙箱启动
        browser = webdriver.Chrome(options=chromeOptions,executable_path="/usr/bin/chromedriver")
        
        browser.get('http://xggl.hnie.edu.cn/content/menu/student/temp/zzdk?_t_s_=1585105573057')
        time.sleep(3)
        
        browser.find_element_by_css_selector(".btn.btn-primary.signin-loader").click()
        time.sleep(1)
        
        browser.find_element_by_id("username").send_keys(_id)
        browser.find_element_by_id("password").send_keys(_password)
        browser.find_element_by_name("submit").click()
        time.sleep(1)
        
        browser.get("http://xggl.hnie.edu.cn/content/menu/student/temp/zzdk?_t_s_=1585201027691")
        time.sleep(3)
        
        browser.find_element_by_id("dk_btn").click()
        time.sleep(10)
        
        browser.find_element_by_id("save").click()
        time.sleep(1)
        
        count += 1
        logstr = "[" + str(count) + "]" +str(time.strftime("%Y-%m-%d %H:%M:%S:" + _name + "打卡成功!", time.localtime()))
        wirtelog(logstr,templogfile)
        
        deluser(_id,tempfile)
        browser.quit()
    except BaseException as err:
        count += 1
        if picfileflag:
            picfile(logsfolder)
            picfileflag = 0
        if "element not interactable" in str(err) or "element not visible" in str(err):
            logstr = "[" + str(count) + "]" +str(time.strftime("%Y-%m-%d %H:%M:%S:" + _name + "打卡失败!  失败原因:", time.localtime())) + "已打卡!"
            wirtelog(logstr,templogfile)
            time.sleep(2)
            piclog(browser,picfolder,_name + "(已打卡)")
            time.sleep(2)
            deluser(_id,tempfile)
            time.sleep(2)
        else:
            errtime = time.strftime("%Y-%m-%d %H:%M:%S:" , time.localtime())
            temperrtime = time.strptime(errtime, "%Y-%m-%d %H:%M:%S:")
            picnametime = time.strftime("(%Y-%m-%d-%H-%M-%S)", temperrtime)
            logstr = "[" + str(count) + "]" + errtime + _name + "打卡失败!  失败原因:" + str(err).replace('\n', '').replace('\r', '')
            time.sleep(2)
            piclog(browser,picfolder,_name+picnametime)
            time.sleep(2)
            wirtelog(logstr,templogfile)
        browser.quit()


def mainfun():
    pool = ThreadPoolExecutor(max_workers=5) #线程池线程数量
    f = open(tempfile,"r",encoding = 'UTF-8')

    for line in f:
        data_list = line.split()
        future1 = pool.submit(run, data_list[0],data_list[1],data_list[2])

    if future1.done() == False:
        time.sleep(15)
    pool.shutdown()
    f.close()


if __name__ == '__main__':
    shutil.copyfile(userfile,tempfile)
    m = 1
    while os.path.getsize(tempfile) and n:
        logstr = "-------------------------------第" + str(m) + "次运行-------------------------------"
        wirtelog(logstr,templogfile)
        mainfun()
        wirtelog('\n',templogfile)
        n -= 1
        m += 1
        count = 0
    #os.remove(tempfile)    #删除副本文件(可有可无,复制文件时会覆盖)
    os.rename(templogfile,logsfolder+"/"+str(time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime()))+ ".txt")

6月4日更新

  由于使用模拟登陆进行打卡,会有多种不确定性因素,比如网络延迟,网络拥堵,网速慢以及学校打卡网站临时关闭等问题,针对这些问题,优化了异常处理(上面的代码已更新)。
  具体过程如下(异常处理过程):
打卡截图1
打卡截图2

(1)所有打卡动作都会进行记录,每次执行打卡任务都会保存日志,如打卡时间、打卡人员、成功与否、失败原因
(2)每次打卡前所有名单先保存到副本文件中,打卡成功人员则从副本文件中删除,打卡失败则会保留,进行下一次打卡,直到全部人员打卡成功(或者达到打卡次数上限)
(3)为了方便打卡失败后排查原因,每次打卡失败则会记录失败原因和当时的截图

6月5日更新

  最开始和室友写这个脚本时还不会用数据库(太菜了,哈哈哈),当时全部的数据都是用txt文档保存的,现在数据已经全部迁移到数据库里面,代码也发生了一些变化
  更新后的主程序如下:

if __name__ == '__main__':
    db = pymysql.connect(host='xxxxxxxxxx',port=xxxx,user='xxxxxxxxxx',password='xxxxxxxxxx',db='xxxxxxxxxx')
    cursor = db.cursor()
    cursor.execute("use xxxxxxxxxx;")
    cursor.execute("select count(*) from xxxxxxxxxx;")
    num = cursor.fetchone()
    cursor.execute("SELECT * FROM xxxxxxxxxx")
    data = cursor.fetchall()
    with open(tempfile,"w",encoding="UTF-8") as f:
        for i in range(0,num[0]):
            f.write(data[i][0]+" "+data[i][1]+" "+data[i][2]+'\n')
    f.close()
    m = 1
    while os.path.getsize(tempfile) and n:
        logstr = "-------------------------------第" + str(m) + "次运行-------------------------------"
        wirtelog(logstr,templogfile)
        mainfun()
        wirtelog('\n',templogfile)
        n -= 1
        m += 1
        count = 0
    os.rename(templogfile,logsfolder+"/"+str(time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime()))+ ".txt")

写在后面

这个打卡脚本是通过模拟浏览器实现的,过程繁琐、对网络要求比较高,而且容易受到网络波动的影响,如果服务器(我的服务器或者学校的服务器)网络状态不好,就很容易引发各种异常,所以导致整个脚本大部分代码都是在处理异常,更好更稳定的自动打卡方式应该是直接发送请求,后续如果有时间的话再写一个用请求自动打卡的脚本(python语言)

更新指路:关于易班打卡那点事(二)

最后修改:2020 年 11 月 17 日 01 : 53 PM
愿君多打钱,此物最相思