python+selenium實現疫情期間全自動打卡
標簽: python
文章目錄
前言
因為疫情緣故,學校搞了個每日限時打卡的系統,要求學生在每天0-9點完成當日體溫和在京狀況的打卡。就這樣手動打卡了兩個多月,北京還是遲遲不開學,目測開學已經要到5月底了。打卡期間忘過無數次,每次都被班長提醒,學校還往家長手機里發送作者沒有打卡的短信,神煩。
于是乎,決定使用 定時開關機軟件 + python 實現一個全自動定時打卡的腳本,省卻我接下來一個月的勞神費力。
學校的打卡系統登錄界面是這樣的:
這里是填寫界面(左側菜單欄需要依次點擊數據采集和學生每日上報才能出現表格,否則是空白頁面。表格很長,注意有縱向和橫向滾動條)
問題分析
手動操作時的步驟是這樣的:
進入學生登錄界面–>輸入賬號密碼
–>進入填報界面–>點擊左側菜單欄–>點擊數據采集–>點擊學生每日上報
–>進入表格–>點擊表格左下角“與昨日情況一致”
–>手動填寫當日體溫–>滑動橫向滾動條–>手動填寫當日在京情況(一直在京)
因此在使用selenium時,其大致步驟與上面描述的無二。
網頁源碼分析與代碼實現
一、加載火狐驅動
selenium需要模擬打開瀏覽器,這里一般使用的瀏覽器驅動(Driver)是谷歌或者火狐,筆者先嘗試了谷歌的驅動,發現在源碼分析的時候效果不是很理想,于是用了火狐的。
(關于驅動的下載與安裝,這里不做贅述,百度即可查看。)
驅動加載并進入后,腳本會自動根據當前的驅動打開對應的火狐瀏覽器,并鏈接到提供url的網頁。
driver = webdriver.Firefox() # 利用火狐瀏覽器
# 填寫疫情上報系統的url
url = r"http://tb.bucea.edu.cn:8075/WebReport/ReportServer?op\=fs_load&cmd=fs_signin&_=1586929099201"
driver.get(url)
# 最大化瀏覽器窗體
driver.maximize_window()
二、輸入賬號密碼并提交
想要實現填寫指定數據并自動填寫到對應的位置,或者點擊按鈕提交到對應位置,就需要通過網頁的各種標簽的id
或者class
來定位相對應的元素,學過web編程的同學應該比較好理解這點。
例如下面這段用于填寫用戶名的代碼
<div class="fs-login-input fs-login-input-username">
<input tabindex="1" class="fs-login-username" type="text" placeholder="用戶名" title="用戶名">
</div>
我們可以看到,這部分的核心在于<input>
標簽,此標簽沒有指定id
,而是給了class
,因此可以使用selenium提供的driver.find_element_by_class_name()
方法,通過class
的值來定位元素,進而調用send_keys()
方法來實現數據填充。
類似的,對于“點擊”操作,只需要獲取<button>
或<radio>
或<a>
標簽的class或者id,進而調用onclick()
方法達到點擊目的。
關于如何定位元素,可以參考這篇文章《史上最全!Selenium元素定位的30種方式》
筆記本上使用Fn+F12可以啟用源代碼查看器,點擊頁面中某個部分,便能自動鎖定相應代碼,非常好用。我們的頁面是下圖這樣的,因此可以通過這段代碼來定位“輸入用戶名”這個元素并設置數據:
elem = driver.find_element_by_class_name('fs-login-username')
elem.send_keys(stu_number)
# 疫情打卡系統url
url = r"http://tb.bucea.edu.cn:8075/WebReport/ReportServer?op=fs_load&cmd=fs_signin&_=1586929099201"
driver.get(url)
driver.maximize_window()
# 填寫用戶名和密碼
elem = driver.find_element_by_class_name('fs-login-username')
elem.send_keys(stu_number)
elem = driver.find_element_by_class_name('fs-login-password')
elem.send_keys(stu_password)
# 提交表單
driver.find_element_by_xpath("//*[@id='fs-login-btn']").click()
三、進入打卡界面并點擊左側菜單欄
點擊提交且用戶驗證成功后,會跳轉到另外一個url,這個界面用于填寫當日身體情況。依次點擊左側菜單欄的數據采集->學生每日上報,可以進入表格界面,如圖:
在第二節的分析中,筆者已經確定了定位元素的基本流程,按照selenium給定的方法,便可依次點開左側菜單欄(這里每執行一步后,盡量使用
time.sleep()
讓程序休息一小段時間,否則頁面可能會卡住或者代碼執行無效)。
但是! 隨之而來的是困擾了我一天的地方。想要定位表格窗體的元素時,卻怎么都定位不到,我試了好多地方,發現只有表格窗體中的元素是無法定位的。
1.iframe內元素的定位
學過web的同學知道,這種頁面的header和菜單欄基本是固定的,通過內嵌<iframe>
或者<frame>
的方式,可以達到不同頁面在相同url中切換、而指定部分(例如菜單欄 表頭等)不變的目的。
于是我猜測元素定位不到可能與<iframe>
的引入有關,也就是說,菜單欄和表格窗體構成的頁面并非一個整體,而是兩個模塊的拼接。遂檢索相關文章,發現果然如此。
如果頁面使用了<iframe>
,想要定位內嵌界面中的某個元素,在編寫代碼并調用selenium時,需要進入相應的<iframe>
內!
知道了這點,就很容易解決了:Fn+F12查看頁面源代碼,把鼠標放在滾動條上就能查看到內嵌頁面的<iframe>
的id或者class,耐心網上翻一點,就能找到表格所在的iframe了:fs_tab_1587094181588
我興沖沖的編寫代碼進入iframe并重啟程序測試:
driver.switch_to.frame(driver.find_element_by_xpath("fs_tab_1587094181588"))
2.動態id/class的定位
what?找不到iframe?
不慌,盯著iframe的idfs_tab_1587094181588
仔細思考了一下,后面一長串的數字像是動態生成的,而這種id一般前面的頭部是不變的,即fs_tab_
,于是我重新查看新頁面的iframe的id,果然和上次打開的不一樣了:fs_tab_1587094408952,表頭也果然沒變。
查看源碼中以fs_tab_
開頭的id,發現只有一個,妙哉,可以使用了。
于是,通過driver.find_element_by_xpath("//iframe[starts-with(@id, 'fs_tab_')]")
模糊匹配帶有fs_tab_
開頭的id,并使用driver.switch_to.frame()
進入相應的iframe,重新定位元素,成功了!
# 由于是動態的id和class,因此...
driver.switch_to.frame(driver.find_element_by_xpath("//iframe[starts-with(@id, 'fs_tab_')]"))
# 點擊“與昨日情況一致”radio
driver.find_elements_by_class_name('fr-group-span')[0].click()
四、表格填寫
經過上面這些分析后,下面的步驟就變得輕松多了。
由于每天填寫的表格行數都是在動態變化的,比如今天的某個單元格id是D21-0-0,明天就變成了D-22-0-0,因此,對于獲取當前日期應該填寫的表格id,可以這樣做:
根據日期差值與編寫代碼時的表格id相加,便得到了當日動態表格id
def getDateDiff():
# 2020-4-16編寫,此時動態列數為21
retire_day = datetime(2020, 4, 16)
today = datetime.now()
left_days = (retire_day - today).days # 獲取兩個日期的天數差值
return abs(left_days)
curColID = str(getDateDiff() + 20) + r'-0-0'
inputCol = ['D', 'AB']
點擊“與昨日情況一致”后,還需要手動填寫兩個表格,分別是當日體溫36度和“一直在京”,不怕,寫個循環就搞定了:
# 列ID形式為:列編號-0-0
curColID = str(getDateDiff() + 20) + r'-0-0'
inputCol = ['D', 'AB']
inputVal = ['36', '一直在京']
for i in range(len(inputCol)):
inputBox = driver.find_element_by_id(inputCol[i] + curColID)
driver.execute_script("arguments[0].scrollIntoView();", inputBox)
time.sleep(1)
# 開始模擬鼠標雙擊操作,不然無法鎖定表格填數據
action_chains = ActionChains(driver)
action_chains.double_click(inputBox).perform()
time.sleep(1)
# 填寫表格
driver.find_element_by_xpath("//input[contains(@class,'fr-texteditor')]").send_keys(inputVal[i])
time.sleep(1)
五、提交表格
一行代碼:
# css選擇器選擇button .x-emb-submit 這是關鍵
print(driver.find_element_by_css_selector('.x-emb-submit').click())
全部代碼
相關代碼已放到我的GitHub
歡迎fork和star!
奉上全部代碼,有需要的同學可以參考:
# encoding=utf8
import sys
import math
from datetime import datetime
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import time
stu_number = ['stu1_number', 'stu2_number']
stu_password = ['stu1_pwd', 'stu2_pwd']
# 獲得日期差值,由于表格數據是動態變化的,可根據編寫日期的行號和日期差來確定當日行號
def getDateDiff():
# 2020-4-16編寫,此時動態列數為21
retire_day = datetime(2020, 4, 16)
today = datetime.now()
left_days = (retire_day - today).days # 獲取兩個日期的天數差值
return abs(left_days)
# 授權操作
def operationAuth(driver):
# time.sleep(10)
try:
# 疫情打卡系統url
url = r"http://tb.bucea.edu.cn:8075/WebReport/ReportServer?op=fs_load&cmd=fs_signin&_=1586929099201"
for j in range(len(stu_number)):
driver.get(url)
driver.maximize_window()
# 填寫用戶名和密碼
elem = driver.find_element_by_class_name('fs-login-username')
elem.send_keys(stu_number[j])
elem = driver.find_element_by_class_name('fs-login-password')
elem.send_keys(stu_password[j])
# 提交表單
driver.find_element_by_xpath("//*[@id='fs-login-btn']").click()
# 沒有睡眠時間就不行switch_to.frame()
time.sleep(2)
# 模擬點擊進入填報表格
driver.find_element_by_link_text("數據采集").click()
driver.find_element_by_link_text("學生每日上報").click()
time.sleep(2)
# 這地方寫博客記錄,由于是動態的id和class,因此...
driver.switch_to.frame(driver.find_element_by_xpath("//iframe[starts-with(@id, 'fs_tab_')]"))
# 點擊“與昨日情況一致”radio
driver.find_elements_by_class_name('fr-group-span')[0].click()
# 列ID形式為:列編號-0-0
curColID = str(getDateDiff() + 20) + r'-0-0'
inputCol = ['D', 'AB']
inputVal = ['36', '一直在京']
for i in range(len(inputCol)):
inputBox = driver.find_element_by_id(inputCol[i] + curColID)
driver.execute_script("arguments[0].scrollIntoView();", inputBox)
time.sleep(1)
# 開始模擬鼠標雙擊操作,不然無法鎖定表格填數據
action_chains = ActionChains(driver)
action_chains.double_click(inputBox).perform()
time.sleep(1)
# 填寫體溫度數
driver.find_element_by_xpath("//input[contains(@class,'fr-texteditor')]").send_keys(inputVal[i])
time.sleep(1)
# css選擇器選擇button .x-emb-submit 這是關鍵
print(driver.find_element_by_css_selector('.x-emb-submit').click())
time.sleep(1)
# driver.close()
except TimeoutException:
# 報錯后就強制停止加載
# 這里是js控制
driver.execute_script('window.stop()')
print(driver.page_source)
# 方法主入口
if __name__ == '__main__':
# 加啟動配置
driver = webdriver.Firefox() # 利用火狐瀏覽器
# driver.get("http://www.baidu.com") # 打開get到的網址
operationAuth(driver)
智能推薦
利用 JS 腳本實現網頁全自動秒殺搶購
利用 JS 腳本實現網頁全自動秒殺搶購 倒計時頁面: 倒計時未結束時,購買按鈕還不能點擊。 結束時,可以點擊購買,點擊后出現提示“付款成功” 展示效果 1.制作測試網頁 首先我們來做一個簡易的搶購頁面 展示一下: 2.倒計時及購買功能的實現 我們使用的是jQuery框架,jQuery 極大地簡化了 JavaScript 編程。 3.使用 JS 腳本實現自動搶購功能 (1)在...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
猜你喜歡
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...
統計學習方法 - 樸素貝葉斯
引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...
styled-components —— React 中的 CSS 最佳實踐
https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...