• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 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)
    
    
    版權聲明:本文為cprimesplus原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/cprimesplus/article/details/105574627

    智能推薦

    利用 JS 腳本實現網頁全自動秒殺搶購

    利用 JS 腳本實現網頁全自動秒殺搶購 倒計時頁面: 倒計時未結束時,購買按鈕還不能點擊。 結束時,可以點擊購買,點擊后出現提示“付款成功” 展示效果 1.制作測試網頁 首先我們來做一個簡易的搶購頁面 展示一下: 2.倒計時及購買功能的實現 我們使用的是jQuery框架,jQuery 極大地簡化了 JavaScript 編程。 3.使用 JS 腳本實現自動搶購功能 (1)在...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    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 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    基于TCP/IP的網絡聊天室用Java來實現

    基于TCP/IP的網絡聊天室實現 開發工具:eclipse 開發環境:jdk1.8 發送端 接收端 工具類 運行截圖...

    精品国产乱码久久久久久蜜桃不卡