學校體溫自動上傳爬蟲

更新 發佈閱讀 21 分鐘

動機

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

raw-image

說實話我看到這個東東我有點驚訝又覺得很好笑??,於是我就開始想解決它。


製作

使用語言*: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個數字就是我要的驗證數字

raw-image

於是我將其存到變數中,以便之後使用。

tag_verification_num=soup.find(id='ContentPlaceHolder1_lbN') 
num_verification=str(tag_verification_num)
num_verification=num_verification[65]

現在我已經取得驗證數字了,接下來我需要取得每個驗證選項的數字。
因為驗證選項的html結構是用span標籤把input選項和顯示選項數字的label包起來。

raw-image
raw-image

所以我先用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學校又再次做出了令我意外的改動.....

raw-image


驗證換成了隨機的問題,學校這波操作再次讓我感到驚喜,建議下次驗證改成解微積分。


思路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()








留言
avatar-img
The shy of bush的沙龍
0會員
1內容數
你可能也想看
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
網路爬蟲的由來 網路爬蟲,也叫網路蜘蛛(spider),是一種用來自動瀏覽全球資訊網的網路機器人。其目的一般為編纂網路索引。網路搜尋引擎等站點通過爬蟲軟體更新自身的網站內容或其對其他網站的索引。網路爬蟲可以將自己所訪問的頁面儲存下來,以便搜尋引擎事後生成索引供使用者搜尋。 網路爬蟲的原理
Thumbnail
網路爬蟲的由來 網路爬蟲,也叫網路蜘蛛(spider),是一種用來自動瀏覽全球資訊網的網路機器人。其目的一般為編纂網路索引。網路搜尋引擎等站點通過爬蟲軟體更新自身的網站內容或其對其他網站的索引。網路爬蟲可以將自己所訪問的頁面儲存下來,以便搜尋引擎事後生成索引供使用者搜尋。 網路爬蟲的原理
Thumbnail
你知道嗎?網路爬蟲其實是整個搜尋世界的起點。爬蟲將所有上線的網頁快速瀏覽後並整理重點做索引,這樣搜尋引擎才能快速檢索相關內容。今天本男爵就來跟大家好好聊一下,在網路世界中無所不在,但你可能卻從未察覺到的這條「蟲」。
Thumbnail
你知道嗎?網路爬蟲其實是整個搜尋世界的起點。爬蟲將所有上線的網頁快速瀏覽後並整理重點做索引,這樣搜尋引擎才能快速檢索相關內容。今天本男爵就來跟大家好好聊一下,在網路世界中無所不在,但你可能卻從未察覺到的這條「蟲」。
Thumbnail
學校的夜間生態課 本來以為要觀星 結果有點下雨 就走步道找找探探 雨蛙、蟾蜍、飛鼠、蚯蚓、黑冠麻鷺 探險完一起認識老師帶來的蛇蛇們 無毒的可以親密接觸 有毒的在罐子裡讓大家近距離觀察 第一次摸蛇蛇 居然不是以為的溼滑觸感(刻板印象) 是乾爽絲滑的 超~級好摸 眼睛圓圓意外的可
Thumbnail
學校的夜間生態課 本來以為要觀星 結果有點下雨 就走步道找找探探 雨蛙、蟾蜍、飛鼠、蚯蚓、黑冠麻鷺 探險完一起認識老師帶來的蛇蛇們 無毒的可以親密接觸 有毒的在罐子裡讓大家近距離觀察 第一次摸蛇蛇 居然不是以為的溼滑觸感(刻板印象) 是乾爽絲滑的 超~級好摸 眼睛圓圓意外的可
Thumbnail
2016/8/28-9/3   處暑之後,像打開了蒸籠的蓋子,氣温驟降了近十度。傍晚在城東濕地公園跑了一圈,捨不得離開,又走了一圈。濕地公園裡面掛了很多綠紗网做的捕蠅器,它的原理是利用蒼蠅習慣往上飛的特性,讓蒼蠅飛進漏斗型的孔,却飛不出去,最終餓死。蒼蠅的眼睛反應靈敏,但不聰明,我發
Thumbnail
2016/8/28-9/3   處暑之後,像打開了蒸籠的蓋子,氣温驟降了近十度。傍晚在城東濕地公園跑了一圈,捨不得離開,又走了一圈。濕地公園裡面掛了很多綠紗网做的捕蠅器,它的原理是利用蒼蠅習慣往上飛的特性,讓蒼蠅飛進漏斗型的孔,却飛不出去,最終餓死。蒼蠅的眼睛反應靈敏,但不聰明,我發
Thumbnail
# 在食蟲植物社團看到版友分享國立自然科學博物館內有食蟲植物進駐販賣的消息,然後就跑了一趟。 # 在攤販前從左邊走到右邊, 再從右邊走回左邊。 一下順時針繞繞, 一下逆時針繞繞。 (繞到店員都跑來關心了XD) # 每一盆都好喜歡~~
Thumbnail
# 在食蟲植物社團看到版友分享國立自然科學博物館內有食蟲植物進駐販賣的消息,然後就跑了一趟。 # 在攤販前從左邊走到右邊, 再從右邊走回左邊。 一下順時針繞繞, 一下逆時針繞繞。 (繞到店員都跑來關心了XD) # 每一盆都好喜歡~~
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News