├── README.md ├── in_school.py └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # 北航每日自动打卡脚本 v1.1 2 | 3 | 虽然到了学校还是没听打卡一次,但是我根本记不住啊!!不做人了!自动打卡机器上线! 4 | 5 | 功能列表: 6 | - 在校打卡、不在校打卡 7 | - 自定义定位 8 | - 检测登录是否成功和网页能否打开 9 | - 定时自动打卡* 10 | - 微信推送打卡结果** 11 | - 日志记录 12 | 13 | \* 定时自动打卡需要程序持续开启,退出或关机无效,建议部署到服务器如阿里云等。 14 | 15 | \** 微信推送结果需要http://sc.ftqq.com/ 支持,免费申请 16 | 17 | 18 | 本脚本基于chrome和selenium,原理为模拟浏览器点击,支持linux系统部署。部分代码参考自https://github.com/buaalzm/fuckdaka 以及 https://github.com/colasama/buaa-ncov-hitcarder ,两者各有一些不足之处,我整合了一下。 19 | 20 | 21 | ## 部署 22 | ### windows 23 | 1、下载谷歌浏览器并查看版本 24 | 25 | 2、```pip install requests, selenium``` 26 | 27 | 3、下载对应版本Chromedriver:https://chromedriver.chromium.org/downloads 28 | 29 | 4、放到一个环境变量能找到的位置或者配置该位置为环境变量(自行百度) 30 | 31 | 5、在python中测试 32 | ``` 33 | from selenium import webdriver 34 | browser = webdriver.Chrome() 35 | browser.get("https://www.baidu.com/") 36 | ```` 37 | 6、下载main.py或in_school.py并修改其中关键信息,运行 38 | ``` 39 | python main.py 40 | ``` 41 | 42 | ###centOS 43 | 1、安装chrome 44 | 45 | ```yum install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm``` 46 | 47 | 2、确认chrome版本: 48 | ```google-chrome --version``` 49 | 50 | 3、根据版本下载Chromedriver: https://chromedriver.chromium.org/downloads 51 | 52 | 4、将下载的文件解压之后的```chromedriver```移动到```/usr/bin```下,并给予执行权限 53 | 54 | ```chmod +x /usr/bin/chromedriver``` 55 | 56 | 5、同windows 57 | 58 | 6、同windows 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /in_school.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from time import sleep 3 | import time 4 | import datetime 5 | import requests 6 | from selenium import webdriver 7 | from selenium.webdriver.chrome.options import Options 8 | from selenium.webdriver.common.action_chains import ActionChains 9 | from selenium.webdriver.common.by import By 10 | from selenium.webdriver.support.ui import WebDriverWait 11 | from selenium.webdriver.support import expected_conditions as EC 12 | 13 | user = "" # 你的统一认证账号 14 | passwd = "" # 你的统一认证密码json 15 | position = ("39.97805900941237", "116.34515751812742") # 定位,经纬度, 这个经纬度是大运村一号楼 16 | SCKEY = "" # 微信推送api,到http://sc.ftqq.com/ 免费申请,不需要请留空 17 | set_time = [(18, 12)] # (小时,分钟), 如果多个时间可以写成[(h, m),(h, m)]的形式 18 | max_attempt = 5 # 失败重复五次 19 | 20 | def daka(): 21 | login_flag, browser = login() 22 | if not login_flag: 23 | return 24 | 25 | browser.execute_script("window.navigator.geolocation.getCurrentPosition=function(success){" + 26 | "var position = {\"coords\" : {\"latitude\": \"" + position[0] + "\",\"longitude\": \"" 27 | + position[1] + "\"}};" + 28 | "success(position);}") 29 | time.sleep(5) 30 | 31 | # /html/body/div[1]/div[1]/div[2]/div[5]/div/div[2]/div[2]/div[2]/input 32 | location_button = browser.find_element_by_css_selector('body > div.buaaStudentNcov > div.buaaStudentNcov-bg > div > div:nth-child(5) > div > div.warp-list-choose > div.warp-list.two-warp-list.warp-list-margin > div.title-input.title-input-mergin-left') 33 | browser.save_screenshot('location_test.png') 34 | ActionChains(browser).move_to_element(location_button).click(location_button).perform() 35 | time.sleep(2) 36 | location = location_button.get_attribute('value') 37 | logger.info(f"成功输入经纬度,定位{location}") 38 | 39 | # /html/body/div[1]/div[1]/div[2]/div[6] 40 | # 点击提交 41 | submit_button = browser.find_element_by_xpath( 42 | '/html/body/div[1]/div[1]/div/div[6]/a') 43 | submit_button.click() 44 | 45 | browser.implicitly_wait(3) 46 | while True: 47 | try: 48 | if submit_button.text.find("submitted") != -1: 49 | result = "打卡失败,您已经提交过" 50 | break 51 | elif submit_button.text.find("未到填报时间") != -1: 52 | result = "未到填报时间" 53 | break 54 | submit_button = browser.find_element_by_css_selector('body > #wapcf > div > div.wapcf-btn-box > div.wapcf-btn-ok') 55 | submit_button.click() 56 | body = browser.find_element_by_css_selector('body') 57 | confirm_button = browser.find_element_by_css_selector('body > #wapcf > div > div.wapcf-btn-box > div') 58 | result = '提交成功' 59 | break 60 | except Exception as e: 61 | try: 62 | confirm_button = browser.find_element_by_css_selector('#wapat > div > div.wapat-btn-box > div') 63 | reason = browser.find_element_by_css_selector('#wapat > div > div.wapat-title').text 64 | result = f'打卡失败,原因:{reason}' 65 | break 66 | except: 67 | time.sleep(1) 68 | 69 | # confirm_button.click() 70 | 71 | logger.info(result) 72 | 73 | datee = datetime.date.today() 74 | 75 | send_message(f"{datee} {result}") 76 | sleep(50) 77 | browser.quit() 78 | logger.info("流程结束") 79 | 80 | 81 | def login(): 82 | chrome_options = Options() 83 | chrome_options.add_argument('--headless') 84 | chrome_options.add_argument('--disable-gpu') 85 | chrome_options.add_argument('--no-sandbox') 86 | browser = webdriver.Chrome(chrome_options=chrome_options) 87 | 88 | try: 89 | url = "https://app.buaa.edu.cn/uc/wap/login?redirect=https%3A%2F%2Fapp.buaa.edu.cn%2Fsite%2FbuaaStudentNcov%2Findex" 90 | browser.get(url) 91 | 92 | # 账号密码 93 | user_name_input = browser.find_element_by_css_selector('#app > div.content > div:nth-child(1) > input[type=text]') 94 | user_name_input.send_keys(user) 95 | user_pwd_input = browser.find_element_by_css_selector( 96 | '#app > div.content > div:nth-child(2) > input[type=password]') 97 | user_pwd_input.send_keys(passwd) 98 | 99 | except: 100 | logger.info("打开打卡网页失败,请确认网络") 101 | send_message("打开打卡网页失败,请确认网络") 102 | return False, None 103 | 104 | logger.info("成功打开打卡网页") 105 | 106 | # 点击登录按钮 107 | login_button = browser.find_element_by_css_selector('#app > div.btn') 108 | ActionChains(browser).move_to_element(login_button).click(login_button).perform() 109 | browser.implicitly_wait(2) 110 | 111 | # 跳转并点击获取位置按钮 112 | # 这样写是为了等待跳转页面加载出来 113 | fail_cnt = 0 114 | while True: 115 | location_button = browser.find_elements_by_css_selector('body > div.buaaStudentNcov > div.buaaStudentNcov-bg > div > div:nth-child(5) > div > div.warp-list-choose > div.warp-list.two-warp-list.warp-list-margin > div.title-input.title-input-mergin-left') 116 | if len(location_button) > 0: 117 | logger.info("登录成功") 118 | return True, browser 119 | else: 120 | # 出现密码错误提示框 121 | if len(browser.find_elements_by_css_selector('div.wapat-btn-box')) > 0: 122 | send_message("打卡失败,用户名密码错误,程序已退出,请检查") 123 | logger.info("打卡失败,用户名密码错误,请检查") 124 | exit(0) 125 | 126 | # 若只是反应慢,重试 127 | if fail_cnt >= max_attempt: 128 | send_message("登录超时超过最大尝试次数,请检查网络或打卡系统已崩溃") 129 | logger.info("登录超时超过最大尝试次数") 130 | return False, None 131 | time.sleep(10) 132 | browser.get("https://app.buaa.edu.cn/site/buaaStudentNcov/index") 133 | logger.info("登录超时,正在重试") 134 | fail_cnt += 1 135 | 136 | 137 | def main(): # 0:05进行打卡 138 | logger.info("正在进行验证...") 139 | flag, browser = login() # 测试能否进入网页以及用户名密码是否正确 140 | browser.quit() 141 | if not flag: 142 | exit(0) 143 | while True: 144 | while True: 145 | # time_up = True # debug 146 | time_up = False 147 | now = datetime.datetime.now() 148 | for hour, minute in set_time: 149 | if now.hour == hour and now.minute == minute: 150 | time_up = True 151 | if time_up: 152 | break 153 | logger.debug(f"时间未到,当前时间 {now}") 154 | sleep(20) 155 | logger.info("时间已到,正在打卡") 156 | daka() 157 | 158 | 159 | def send_message(msg): 160 | if SCKEY == "": 161 | return 162 | payload = {'text': msg} 163 | requests.get(f"https://sc.ftqq.com/{SCKEY}.send", params=payload) 164 | 165 | 166 | if __name__ == "__main__": 167 | log_file = "log.log" 168 | formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") 169 | logger = logging.getLogger("main") 170 | fh = logging.FileHandler(log_file, mode='w') 171 | fh.setFormatter(formatter) 172 | ch = logging.StreamHandler() 173 | ch.setFormatter(formatter) 174 | logger.addHandler(fh) 175 | logger.addHandler(ch) 176 | logger.setLevel(logging.INFO) 177 | main() 178 | 179 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from time import sleep 3 | import time 4 | import datetime 5 | import requests 6 | from selenium import webdriver 7 | from selenium.webdriver.chrome.options import Options 8 | from selenium.webdriver.common.action_chains import ActionChains 9 | from selenium.webdriver.common.by import By 10 | from selenium.webdriver.support.ui import WebDriverWait 11 | from selenium.webdriver.support import expected_conditions as EC 12 | 13 | user = "" # 你的统一认证账号 14 | passwd = "" # 你的统一认证密码json 15 | position = ("33.3333333", "111.11111") # 定位,(纬度, 经度) 16 | SCKEY = "" # 微信推送api,到http://sc.ftqq.com/ 免费申请,不需要请留空 17 | 18 | set_time = [(0, 5)] # (小时,分钟), 如果多个时间可以写成[(h, m),(h, m)]的形式 19 | max_attempt = 5 # 失败重复五次 20 | 21 | 22 | def daka(): 23 | login_flag, browser = login() 24 | if not login_flag: 25 | return 26 | 27 | browser.execute_script("window.navigator.geolocation.getCurrentPosition=function(success){" + 28 | "var position = {\"coords\" : {\"latitude\": \"" + position[0] + "\",\"longitude\": \"" 29 | + position[1] + "\"}};" + 30 | "success(position);}") 31 | 32 | location_button = browser.find_element_by_css_selector('div[name=area]>input') 33 | location_button.click() 34 | logger.info("成功输入经纬度") 35 | # 点击提交 36 | submit_button = browser.find_element_by_css_selector( 37 | 'body > div.item-buydate.form-detail2 > div > div > section > div.list-box > div > a') 38 | ActionChains(browser).move_to_element(submit_button).click(submit_button).perform() 39 | 40 | browser.implicitly_wait(3) 41 | while True: 42 | try: 43 | confirm_button = browser.find_element_by_css_selector('#wapcf > div > div.wapcf-btn-box > div.wapcf-btn.wapcf-btn-ok') 44 | result = '提交成功' 45 | break 46 | except: 47 | try: 48 | confirm_button = browser.find_element_by_css_selector('#wapat > div > div.wapat-btn-box > div') 49 | reason = browser.find_element_by_css_selector('#wapat > div > div.wapat-title').text 50 | result = f'打卡失败,原因:{reason}' 51 | break 52 | except: 53 | time.sleep(1) 54 | 55 | ActionChains(browser).move_to_element(confirm_button).click(confirm_button).perform() 56 | 57 | logger.info(result) 58 | 59 | datee = datetime.date.today() 60 | 61 | send_message(f"{datee} {result}") 62 | sleep(50) 63 | browser.quit() 64 | logger.info("流程结束") 65 | 66 | 67 | def login(): 68 | chrome_options = Options() 69 | chrome_options.add_argument('--headless') 70 | chrome_options.add_argument('--disable-gpu') 71 | chrome_options.add_argument('--no-sandbox') 72 | browser = webdriver.Chrome(chrome_options=chrome_options) 73 | 74 | try: 75 | url = "https://app.buaa.edu.cn/uc/wap/login" 76 | browser.get(url) 77 | 78 | # 账号密码 79 | user_name_input = browser.find_element_by_css_selector('#app > div.content > div:nth-child(1) > input[type=text]') 80 | user_name_input.send_keys(user) 81 | user_pwd_input = browser.find_element_by_css_selector( 82 | '#app > div.content > div:nth-child(2) > input[type=password]') 83 | user_pwd_input.send_keys(passwd) 84 | 85 | except: 86 | logger.info("打开打卡网页失败,请确认网络") 87 | send_message("打开打卡网页失败,请确认网络") 88 | return False, None 89 | 90 | logger.info("成功打开打卡网页") 91 | 92 | # 点击登录按钮 93 | login_button = browser.find_element_by_css_selector('#app > div.btn') 94 | ActionChains(browser).move_to_element(login_button).click(login_button).perform() 95 | browser.implicitly_wait(2) 96 | 97 | # 跳转并点击获取位置按钮 98 | # 这样写是为了等待跳转页面加载出来 99 | fail_cnt = 0 100 | while True: 101 | location_button = browser.find_elements_by_css_selector('div[name=area]>input') 102 | if len(location_button) > 0: 103 | logger.info("登录成功") 104 | return True, browser 105 | else: 106 | # 出现密码错误提示框 107 | if len(browser.find_elements_by_css_selector('div.wapat-btn-box')) > 0: 108 | send_message("打卡失败,用户名密码错误,程序已退出,请检查") 109 | logger.info("打卡失败,用户名密码错误,请检查") 110 | exit(0) 111 | 112 | # 若只是反应慢,重试 113 | if fail_cnt >= max_attempt: 114 | send_message("登录超时超过最大尝试次数,请检查网络或打卡系统已崩溃") 115 | logger.info("登录超时超过最大尝试次数") 116 | return False, None 117 | time.sleep(10) 118 | browser.get("https://app.buaa.edu.cn/ncov/wap/default/index") 119 | logger.info("登录超时,正在重试") 120 | fail_cnt += 1 121 | 122 | 123 | def main(): # 0:05进行打卡 124 | logger.info("正在进行验证...") 125 | flag, browser = login() # 测试能否进入网页以及用户名密码是否正确 126 | browser.quit() 127 | if not flag: 128 | exit(0) 129 | while True: 130 | while True: 131 | # time_up = True # debug 132 | time_up = False 133 | now = datetime.datetime.now() 134 | for hour, minute in set_time: 135 | if now.hour == hour and now.minute == minute: 136 | time_up = True 137 | if time_up: 138 | break 139 | logger.debug(f"时间未到,当前时间 {now}") 140 | sleep(20) 141 | logger.info("时间已到,正在打卡") 142 | daka() 143 | 144 | 145 | def send_message(msg): 146 | if SCKEY == "": 147 | return 148 | payload = {'text': msg} 149 | requests.get(f"https://sc.ftqq.com/{SCKEY}.send", params=payload) 150 | 151 | 152 | if __name__ == "__main__": 153 | log_file = "log.log" 154 | formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") 155 | logger = logging.getLogger("main") 156 | fh = logging.FileHandler(log_file, mode='w') 157 | fh.setFormatter(formatter) 158 | ch = logging.StreamHandler() 159 | ch.setFormatter(formatter) 160 | logger.addHandler(fh) 161 | logger.addHandler(ch) 162 | logger.setLevel(logging.INFO) 163 | main() 164 | --------------------------------------------------------------------------------