學校體溫自動上傳爬蟲

更新於 2024/12/16閱讀時間約 21 分鐘

動機

因為疫情,這學習開始學校要求大家每天都要在九點前到學校網站上傳體溫,我覺得非常麻煩。
動點,果沒傳要被記警告!?
這種麻煩的要求我當然是不會每天乖乖上傳的,於是我做了一個小爬蟲來幫我自動上傳體溫。
本來每天用的好好,但有一天我發現爬蟲沒有上傳成功,於是我只能手動上傳,但當我打開網站要上傳時,我看到了令我無比傻眼的畫面……….????????!
說實話我看到這個東東我有點驚訝又覺得很好笑??,於是我就開始想解決它。

製作

使用語言*:python
使用套件:
import requests 

from bs4 import BeautifulSoup, element
from selenium 
import webdriver
import selenium
import time
首先,我用requests和把網站爬下來,然後丟到BeautifulSoup裡剖析。
url=’https://webap1.kshs.kh.edu.tw/kshsSSO/publicWebAP/bodyTemp/index.aspx'
r=requests.get(url)
soup=BeautifulSoup(r.text,’lxml’)
再來,我的任務是讓電腦自動判斷驗證的數字,並勾相應的選項。
取得驗證數字有問題的方式
我把BeautifulSoup剖析後的網頁,用find$=(id=''ContentPlaceHolder1_lb)的方式找出html中顯示驗證數字的span(區域)並將tag_verification_num.string(驗證數字)存到tag_verification_num變數中。
但問題來了,當我把tag_verification_num這個變數print出來時,它的值是null。
解決方法
把BeautifulSoup剖析後的網頁,用find$=(id='ContentPlaceHolder1_lb')的方式找出html中顯示驗證數字的span(區域)並將tag_verification_num.string(驗證數字)存到tag_verification_num變數後,將它轉成str(字串型態),在這裡我發現這個span轉成字串後的第65個數字就是我要的驗證數字
於是我將其存到變數中,以便之後使用。
tag_verification_num=soup.find(id='ContentPlaceHolder1_lbN') 
num_verification=str(tag_verification_num) 
num_verification=num_verification[65]
現在我已經取得驗證數字了,接下來我需要取得每個驗證選項的數字。
因為驗證選項的html結構是用span標籤把input選項和顯示選項數字的label包起來。
所以我先用find.(id=’ContentPlaceHolder1_RadioButtonList1')找到input和label的父標籤span,存到tag_verification變數中,再用tag_verification.find_all(‘input’)找到所有span裡的input標籤。
tag_verification=soup.find(id=’ContentPlaceHolder1_RadioButtonList1')
tag_verification_input=tag_verification.find_all(‘input’)
錯誤取得選項值的方式
用tag_verification.find_all(‘input’)取得所有span裡的input標籤後,再用
for i in tag_verification_input:存取每個input標籤的value值,但是這邊我遇到跟前面很像的問題,就是當我用print(i.value)把它print出來的值是null。
解決方法
這裡也可以用跟上面一樣的方式,把input標籤轉成字串型態,在找到value的值是字串中的第幾個字元,這樣就可以取得驗證選項的數字。
tag_verification=soup.find(id=’ContentPlaceHolder1_RadioButtonList1')
tag_verification_input=tag_verification.find_all(‘input’)
for i in tag_verification_input:
   num_input=str(i)
   num_input[121]//驗證選項的數字
因為tag_verification_input裡面的input子標籤在for迴圈裡面是用i代替,所以以下的驗證選項input標籤,都用i代替。
現在我有了驗證的數字,也有了每個選項的數字,那我現在要做的是判斷哪一個選項的數字等於驗證數字。
我在for i in tag_verification_input:裡面加上判斷式,來判斷i轉成字串後的第121個字元(驗證選項的數字)是否等於num_verification(驗證數字),
for i in tag_verification_input:
num_input=str(i)
  if num_verification==num_input[121]:

  //填報體溫
我已經知道對應到驗證數字的input標籤了,現在我要知道這個input標籤的name屬性,才能用find_element_by_name()控制webdriver勾選正確選項。
如果用最直覺的方法num_input=i.name,那num_input的值一樣會是null,所以還是要把i轉成字串,然後num_input[57:99]存取第59~99之間字元,這個就是i的name。
得到input標籤的name之後,就可以控制webdriver勾選正確選項了~

Code

import requests  
from bs4 import BeautifulSoup, element 
from requests.models import Response 
from selenium 
import webdriver 
import selenium 
import time  
url='https://webap1.kshs.kh.edu.tw/kshsSSO/publicWebAP/bodyTemp/index.aspx'
r=requests.get(url) soup=BeautifulSoup(r.text,'lxml')  
driver=webdriver.Chrome("./chromedriver") 
driver.get(url)  
tag_verification_num=soup.find(id='ContentPlaceHolder1_lbN') 
num_verification=str(tag_verification_num) 
num_verification=num_verification[65] def enter_step1():     
  tag_input = driver.find_element_by_name("ctl00$ContentPlaceHolder1$txtId")     
  tag_input.send_keys('(身分證字號)')     
  driver.find_element_by_name("ctl00$ContentPlaceHolder1$btnId").click() 
def enter_step2():     
  driver.find_element_by_name('ctl00$ContentPlaceHolder1$rbType').click()     
  driver.find_elenent_by_xpath('//*[@id="ContentPlaceHolder1_ddl1"]/option[3]').click()     
  driver.find_element_by_xpath('//*[@id="ContentPlaceHolder1_ddl2"]/option[3]').click()     
  driver.find_element_by_xpath('//*[@id="ContentPlaceHolder1_ddl3"]/option[2]').click()     
  driver.find_element_by_name('ctl00$ContentPlaceHolder1$btnId0').click() 
  tag_verification=soup.find(id='ContentPlaceHolder1_RadioButtonList1') 
  tag_verification_input=tag_verification.find_all('input')   
for i in tag_verification_input:     
  num_input=str(i)     
  print('迴圈外',num_input[121])     
  if num_verification==num_input[121]:         
    print('要驗證的數字',num_verification)       
    print('要輸入的數字',num_input[121])         
    print('secess\n')         
    print(i)         
    print(type(i),'\n')         
    print(num_input[57:99])         
    print(type(num_input[57:99]))         
    print('選取的數字:',num_input[121])         
    driver.find_element_by_name(num_input[57:99]).click()         
    enter_step1()        
    enter_step2()     
  else:         
    print('fail') 
time.sleep(3) 
driver.quit()

改進

動機2.0
2021/4/21學校又再次做出了令我意外的改動.....
驗證換成了隨機的問題,學校這波操作再次讓我感到驚喜,建議下次驗證改成解微積分。

思路2.0

異想天開
一開始我心想,學校會放的題目因該都是大眾會到的基本知識,那麼維基百科也一定查得到,所我想用requests和
beautifulsoup把題目爬下來,然後再送到維基百科搜尋答案,最後判斷維基百科的答案和網站上選項爬下來的答
案是否相同。
問題:這次驗證方式改版後,我發現已經沒辦法用requests和beautifulsoup選項標籤的任何屬性。
解決方法:能力不足,無解。
暴力解法
後來我老師建議了我一個可以忽略所有標籤的值,和驗證題目的方法。
就是我發現驗證選項的每個input標籤的id屬性前面都是ContentPlaceHolder1_RadioButtonList1_,
而最後一個數字是選項的編號,比如說:
選項一:ContentPlaceHolder1_RadioButtonList1_0
選項二:ContentPlaceHolder1_RadioButtonList1_1
選項三:ContentPlaceHolder1_RadioButtonList1_2
這樣就只需要按照這個規則一個選項一個選項的試,就可以解決了。
function
為了方便,我把除了驗證之外的操作做成function。
  1. enter_step1()
  • 功能:輸入身分證字號和點擊開始填報按鈕。
def enter_step1():    
  print('step1')     
  driver.find_element_by_id('ContentPlaceHolder1_txtId').send_keys('(身分證字號)')     
  driver.find_element_by_id('ContentPlaceHolder1_btnId').click()
2. enter_step2()
  • 功能:填入體溫選項,和出席狀況選項。
def enter_step2():     
  print('step2')     
  driver.find_element_by_id('ContentPlaceHolder1_rbType_1').click()     
  driver.find_element_by_xpath('//*[@id="ContentPlaceHolder1_ddl1"]/option[3]').click()     
  driver.find_element_by_xpath('//*[@id="ContentPlaceHolder1_ddl2"]/option[4]').click()     
  driver.find_element_by_xpath('//*[@id="ContentPlaceHolder1_ddl3"]/option[2]').click()
3. submit()
  • 功能:點擊填報完成按鈕。
def sunmit():     
  print('submit')     
  driver.find_element_by_id('ContentPlaceHolder1_btnId0').click()
驗證
首先,我把所有裝有驗證選項的input標籤的id屬性,去掉代表邊好的最後一個數字,然後存到id變數中。
name='ContentPlaceHolder1_RadioButtonList1_'
因為選項的數量每天不一樣,但是經過我的觀察,選項最多只有4個。
所以我用for i in range(3),然後宣告一個變數等於name+iinput_id=name+str(i),就可以利用for迴圈取得每個input標籤的i屬性,再把這個變數放到find_element_by_id()中,每一圈都試一次。
然後因為如果驗證失敗還繼續執行填報體溫的程式碼的話,就會噴error,所以在for迴圈裡面用例外處理try和except,如果驗證失敗就直接去下一回圈。
因為按下開始填報後,網頁會跳出一個alert說“開始填報”,所以我要switch_to_alert().accept()來確認這個alert。
for i in range(3):     
  try:           
    input_id=name+str(i)         
    print(input_id)         
    driver.find_element_by_id(input_name).click()         
    enter_step1()         
    driver.switch_to_alert().accept()         
    print('success')         e
    nter_step2()         
    # sunmit() 
  except:         
    print('fail')           
    # driver.switch_to_alert().accept()
可能的問題
這樣也不需要擔心,因為如果今天選項只有兩個,卻跑了4個for迴圈,因為如果只有兩個選項,那正確選項也一定在for回圈前兩圈中,程式只要跑到正確選項時,就會直接完成體溫填報。
結論
這樣不管遇到怎樣的驗證問題,都不會影響程式的執行~~
開心。

Code

import requests 
from selenium import webdriver 
import time  
url='https://webap1.kshs.kh.edu.tw/kshsSSO/publicWebAP/bodyTemp/index.aspx'driver=webdriver.Chrome("./chromedriver") 
driver.get(url)  
def enter_step1():    
  print('step1')     
  driver.find_element_by_id('ContentPlaceHolder1_txtId').send_keys('(身分證字號)')     
  driver.find_element_by_id('ContentPlaceHolder1_btnId').click()      
def enter_step2():    
  print('step2')     
  driver.find_element_by_id('ContentPlaceHolder1_rbType_1').click()     
  driver.find_element_by_xpath('//[@id="ContentPlaceHolder1_ddl1"]/option[3]').click()     
  driver.find_element_by_xpath('//[@id="ContentPlaceHolder1_ddl2"]/option[4]').click()    
  driver.find_element_by_xpath('//[@id="ContentPlaceHolder1_ddl3"]/option[2]').click()      
def sunmit():    
  print('submit')     
  driver.find_element_by_id('ContentPlaceHolder1_btnId0').click()  
id='ContentPlaceHolder1_RadioButtonList1_' 
for i in range(3):     
  try:           
    input_id=name+str(i)         
    print(input_id)         
    driver.find_element_by_id(input_name).click()         
    enter_step1()         
    driver.switch_to_alert().accept()         
    print('success')         
    enter_step2()         
    sunmit()     
  except:         
    print('fail') 
time.sleep(5) 
driver.quit()
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
本文介紹了臺灣戒嚴時期文學主題以及四個文學團體的因應方案。透過介紹不同的作家與他們的作品風格取向,突顯出在戒嚴政策的底下,文學界選擇了與政府前呼後應、曲折避開,或直球對決的不一而足的態度。
Thumbnail
什麼?中壢也可以採草莓?我的第一次採摘草莓體驗。 草莓學校提供豐富的採摘體驗與手作課程,還有美味的午餐套餐,值得前往體驗。提供預約制度,只在假日開放,保證草莓的品質。體驗者也可以期待更多元的體驗。一起來感受草莓樂趣吧!
Thumbnail
會踏上這段旅程,主要是因為人生待辦清單中一直有一個「提升英文口說」的項目遲遲沒有完成,既然處在這個閒暇的長假中,想說就好好把握這段時間,做點平常無法輕易行動的事情─到國外住一個月並學英文,就出現在我的決定裡了!
Thumbnail
你的孩子會玩「扮演遊戲」嗎?扮演遊戲對孩子的好處很多,當你的孩子玩起扮演遊戲時,身為父母的你,千萬別錯過了這個幫助孩子學習的好機會!
Thumbnail
這幾天韓國發表新發現常溫超導體的新聞轟動科學界,所以我讀了一下發表最新研究的英文論文,雖然還不確定這個新發現是不是真的,不過我覺得有能力用英文精簡描述這類科學發現的能力也是滿重要的~ 所以我就拆解一下這篇常溫超導體論文摘要的架構跟對應英文句型..
Thumbnail
自由和責任是很重要的平衡,但是這個自由是真正的選擇,而不是好像讓他有選擇但其實沒有,那他就不應該去面對那個責任,就像我們告訴小孩說讀書是學生的本分,國英數自社沒學好是你的責任,聽起來很有道理,但實際上小孩從沒自己選擇自己去哪個學校學哪個科目,這些都是我們為他們安排好的,這是社會讓他們做的。
Thumbnail
21世紀的文盲,不是不會閱讀、寫字的人,而是不懂得學習、不懂得揚棄所學,以及不懂得重新學習的人。 這是摘自艾文‧托佛勒的名言,看到當下有些刺眼,當重新審思這個世代變遷的速度,和取得文憑期間所受的教育學習竟有大程度的斷層,就不得不省思一番了。
Thumbnail
今天參加了女力學院與一起辦的藝術與自我成長體驗活動,身為禪繞畫教師的我,發現與我事先以為的內容,完全搞錯方向!!今天課程收獲滿滿,值得分成上下兩集
Thumbnail
各位同學大家好,我是地瓜校長。 今天,是奇幻的2020下半場的第一天,也是我們正式推翻自己的第一天。 「雜學校」作為一年一度的亞洲最大教育創新博覽會,6年來我們集結了超過1400個屬於台灣教育多樣多元的創新光譜。從2015年那場地表上最有趣的教育展「不太乖教育節」開始,對全台灣徵求各種的非典型教育
Thumbnail
既是以身為一位學生,也是以一位向未知探索的成年人,夾雜在孩子與大人間角色的角度去分享這篇整理過後的文章。 沒有圍牆,沒有門,而是名副其實的「開放式」教育學校。沒有訓導處,只有陪你一起練習正視情緒面對挫折的諮商處。
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
本文介紹了臺灣戒嚴時期文學主題以及四個文學團體的因應方案。透過介紹不同的作家與他們的作品風格取向,突顯出在戒嚴政策的底下,文學界選擇了與政府前呼後應、曲折避開,或直球對決的不一而足的態度。
Thumbnail
什麼?中壢也可以採草莓?我的第一次採摘草莓體驗。 草莓學校提供豐富的採摘體驗與手作課程,還有美味的午餐套餐,值得前往體驗。提供預約制度,只在假日開放,保證草莓的品質。體驗者也可以期待更多元的體驗。一起來感受草莓樂趣吧!
Thumbnail
會踏上這段旅程,主要是因為人生待辦清單中一直有一個「提升英文口說」的項目遲遲沒有完成,既然處在這個閒暇的長假中,想說就好好把握這段時間,做點平常無法輕易行動的事情─到國外住一個月並學英文,就出現在我的決定裡了!
Thumbnail
你的孩子會玩「扮演遊戲」嗎?扮演遊戲對孩子的好處很多,當你的孩子玩起扮演遊戲時,身為父母的你,千萬別錯過了這個幫助孩子學習的好機會!
Thumbnail
這幾天韓國發表新發現常溫超導體的新聞轟動科學界,所以我讀了一下發表最新研究的英文論文,雖然還不確定這個新發現是不是真的,不過我覺得有能力用英文精簡描述這類科學發現的能力也是滿重要的~ 所以我就拆解一下這篇常溫超導體論文摘要的架構跟對應英文句型..
Thumbnail
自由和責任是很重要的平衡,但是這個自由是真正的選擇,而不是好像讓他有選擇但其實沒有,那他就不應該去面對那個責任,就像我們告訴小孩說讀書是學生的本分,國英數自社沒學好是你的責任,聽起來很有道理,但實際上小孩從沒自己選擇自己去哪個學校學哪個科目,這些都是我們為他們安排好的,這是社會讓他們做的。
Thumbnail
21世紀的文盲,不是不會閱讀、寫字的人,而是不懂得學習、不懂得揚棄所學,以及不懂得重新學習的人。 這是摘自艾文‧托佛勒的名言,看到當下有些刺眼,當重新審思這個世代變遷的速度,和取得文憑期間所受的教育學習竟有大程度的斷層,就不得不省思一番了。
Thumbnail
今天參加了女力學院與一起辦的藝術與自我成長體驗活動,身為禪繞畫教師的我,發現與我事先以為的內容,完全搞錯方向!!今天課程收獲滿滿,值得分成上下兩集
Thumbnail
各位同學大家好,我是地瓜校長。 今天,是奇幻的2020下半場的第一天,也是我們正式推翻自己的第一天。 「雜學校」作為一年一度的亞洲最大教育創新博覽會,6年來我們集結了超過1400個屬於台灣教育多樣多元的創新光譜。從2015年那場地表上最有趣的教育展「不太乖教育節」開始,對全台灣徵求各種的非典型教育
Thumbnail
既是以身為一位學生,也是以一位向未知探索的成年人,夾雜在孩子與大人間角色的角度去分享這篇整理過後的文章。 沒有圍牆,沒有門,而是名副其實的「開放式」教育學校。沒有訓導處,只有陪你一起練習正視情緒面對挫折的諮商處。