1. 程式人生 > 其它 >python+selenuim實現web自動打卡

python+selenuim實現web自動打卡

python+selenuim實現web自動打卡

摘要:

​ 大概就是在XX背景下,需要每天登入網站填報體溫,所在地資訊等,分析登入時傳送請求邏輯,通過python-selenium實現自動打卡。

思路

​ 通過selenium操作瀏覽器,完成登入->填充表單資訊->定時重複打卡。

  1. 登入: 使用機器完成登入有兩種方法,一種通過定位驗證碼位置通過ocr識別字符,登入後儲存cookie,之後往瀏覽器中填入cookie即可跳過登入, 其次就是手動維護cookie。分析得到當發起登入請求時,客戶端將生成隨機字串塞入Request-Header的cookie中,ASP.NET_SessionId=ryhrbiqalg14hgrxhmhd4ie5
    ,服務端在Response-Header的Set-Cookie中塞入 CenterSoftWeb=xxxxx; expires=Sun, 23-Jan-2022 07:45:06 GMT; path=/; HttpOnly,由此組成完整的可跳過登入的cookie
  2. 填充表單資訊 通過selenium操作瀏覽器中元素,完成一系列邏輯操作,(需要下載瀏覽器所對應的驅動driver,chrome+chromedriver)
  3. 定時重複打卡 Crontab+shell 即可實現每日定時啟動

關於selenium-python,請到網站檢視

具體實現

​ 定位驗證碼位置,由機器識圖,首先啟動操作selenium在當前執行環境下獲取瀏覽器截圖,再將圖片匯入ps中定位驗證碼四角位置畫素值。

獲取到四角位置時,即可儲存驗證碼圖片傳入ocr機器識別。

#儲存截圖
browser.save_screenshot('verification/'+stunumber+'.png')
#填入資訊
browser.find_element_by_id("StudentId").send_keys(stunumber)
browser.find_element_by_xpath("/html/body/div[1]/div[2]/div/div[2]/form/div[2]/div[2]/input").send_keys(pwd)

#圖片剪下
left = 781
top = 336
right = 870
bottom = 369
picture = Image.open('verification/'+stunumber+'.png')
picture = picture.crop((left, top, right, bottom))
picture.save('verification/'+stunumber+'-ed.png') 

#ocr識別 接入的是api
yzm_code = moyunocr.getYzm('verification/'+stunumber+'-ed.png')

#獲取cookie寫入檔案
dictCookies = browser.get_cookies()   
jsonCookies = json.dumps(dictCookies)
with open('cookies/'+stunumber+'_cookie.txt', 'w') as f:
  f.write(jsonCookies)

儲存的cookie內容

ps:獲取到得Cookie通過json庫物件轉為文字了,因此格式與官方定義的cookie格式略有差別。

[
{"domain": "dxg.cxxu.edu.cn", "expiry": 1615705350, "httpOnly": true, "name": "CenterSoftWeb", "path": "/", "secure": false, "value": "08CBB2BBDCB3E268D3EE9D0C8046B4C979910509ACF95A1F302336B2826DCAD9CE60311213F70E114CAF84ED18695CB3C6260D17B502F8D42ADC8966B6886C5C78D8B479AC5F152E8CC526E9517C1CF3783D3462632A5EDC0104C19C58AFCE9B020937BD766C253F8A327AE72F11F5ECD4F91E72021B3ECC9C7232BFBCAEF0BA38ACD4246EA9CD293CD7ADFD1BC682DF7FEB194CB8E433804D93B3CF00B299E55DA5530BF7D32523977316BAF9486DBAB85221C5348E587F05220BC9A44EE8901C33ADD15097D87E8305C0FEF8A4A3B9D2B358172EA280D622DE78B53CAE5EE88379B2600F38740BD2D33CFC0192CCD9B2DCE5C8BE4124A4466623C8A96B2499B25C16C9EA128D19C88BA0FD8BC4746F"}, 
{"domain": "dxg.cxxu.edu.cn", "httpOnly": true, "name": "ASP.NET_SessionId", "path": "/", "secure": false, "value": "qe45ulzkfahnqjvvexj45kcr"}
]

cookie文件

​ 基於HTTP協議本身是無狀態的,無法辨別每次的請求是否由同一個使用者發起,因此可以通過在每次請求頭中放入可識別狀態的資訊。由此引生出cookie與token等兩種身份驗證方式,可簡單理解為 身份識別器。

​ Cookie發起請求時存在於Request-Header中。

格式:

​ cookie為一組 name=value形式的資料並以';'分割的列表 例:PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1

​ Set-Cookie HTTP響應頭用於將Cookie從伺服器傳送到使用者代理,以便使用者代理稍後可以將其傳送回伺服器。要傳送多個Cookie,應在同一響應中傳送多個Set Cookie頭

Attributes屬性

屬性項 描述 是否必須
cookie-name=cookie-value name自定但要唯一,value依據業務存值
Expires=date 1.cookie的最長生存期,2.不設定則是會話cookie 3.截止日期與設定cookie的客戶端相關,過期刪除
Max-Age=number 類似Expires但優先順序比Expires高
Domain=domain-value 1.定義接收cookie的主機 2.[.example.com]寫法包含子級域名但可能被忽略 3.
Path=path-value 1.cookie生效路徑 URL中必須存在的路徑 2.
Secure 只有在用https進行請求時才將cookie傳送到伺服器
HttpOnly 禁止JavaScript訪問cookie
SameSite=samesite-value 控制cookie是否隨跨源請求一起傳送 可選[Strict(禁止跨域),Lax(在使用者從外部網站導航到原網站時傳送cookie,預設),None(允許跨域,同時設定Secure)]

請求攜帶Cookie

with open('cookies/'+stunumber+'_cookie.txt', 'r', encoding='utf8') as f:
		listCookies = json.loads(f.read())
# 往browser裡新增cookies
for cookie in listCookies:
    cookie_dict = {
    'domain': '.mnnu.edu.cn',
    'name': cookie.get('name'),
    'value': cookie.get('value'),
    "expires": '',
    'path': '/',
    'httpOnly': False,
    'HostOnly': False,
    'Secure': False
    }
		browser.add_cookie(cookie_dict)
browser.refresh()

Linux下selenium配置

前提:由於linux瀏覽器無法介面化,因此在初始化browser時需要額外指定配置

chrome_options = webdriver.ChromeOptions()
# 無頭模式
chrome_options.add_argument('--headless')
# 禁用GPU加速
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(chrome_options=chrome_options, executable_path='/root/chromedriver')
#全域性隱式等待時間
browser.implicitly_wait(10)

指令碼監聽cookie過期

遍歷指定資料夾下cookie檔案的最後一次修改時間與現在時間的間隔,當間隔超過6天時郵件提醒更新

SIX_DAY_SECONDS=6*24*60*60
SEVEN_DAY_SECONDS=7*24*60*60
def listFile(rootpath):
    todayStamp = time.time()
    todayTime = dateFormat(todayStamp)
    logging.info("==========="+todayTime+"====START====")
    fileList = os.listdir(rootpath)
    email_msg =""
    for i in range(0,len(fileList)):
        fullpath = os.path.join(rootpath,fileList[i])
        # 判斷是否是檔案
        if os.path.isfile(fullpath):
            # 獲取最後修改日期
            lastTimeStamp = os.path.getmtime(fullpath)
            # 相差秒數
            diffSeconds = daysDiff(todayStamp,lastTimeStamp)
            if SEVEN_DAY_SECONDS-diffSeconds <=0:
                logging.info(stunumber+ "您的cookie已經過期")
            if diffSeconds<=SEVEN_DAY_SECONDS :
                if diffSeconds>=SIX_DAY_SECONDS :
                    logging.info(stunumber+"您的cookie即將過期 有效時間還剩:"+str((SEVEN_DAY_SECONDS-diffSeconds)//3600)+"小時")
                    # 傳送郵件提醒
                    email_msg+=stunumber+"您的cookie即將過期 有效時間還剩:"+str((SEVEN_DAY_SECONDS-diffSeconds)//3600)+"小時\n"
                else: 
                    logging.info(stunumber+"您的cookie有效時間還剩:"+str((SEVEN_DAY_SECONDS-diffSeconds)//3600)+"小時")