由于公司最近需要對接某業務系統,涉及到部分數據需要提交至其它平臺業務系統,只有其它平臺賬戶,沒有接口,因此做此開發。首先通過OpenCV計算出驗證驗證碼滑塊距離,根據距離,使用 Playwright 利用滑動距離模擬登錄
下面展示Python 通過 Playwright 滑動及登錄過程
- 首先下載 安裝 Playwright
pip install playwright
- 安裝瀏覽器驅動(此步驟會在本地安裝二進制瀏覽器:火狐、谷歌等)
python -m playwright install
- Playwright 錄制腳本(通過腳本錄制,不需要寫代碼,通過鼠標事件將代碼輸出到文件當中)
python -m playwright codegen -o "D:test.py"
- 主要代碼展示
import re
from playwright.sync_api import Playwright, sync_playwright
import time
import random
from result import error_result, success_result
from position import get_gap_positiondef perform_slide(page, max_retries=3):"""執行滑塊驗證,支持自動重試"""retry_count = 0while retry_count < max_retries:try:page.wait_for_selector("#circle", state="visible", timeout=2000)page.wait_for_selector("#bg_canvas", state="visible", timeout=2000)# 計算缺口位置try:image_data = page.evaluate("document.getElementById('bg_canvas').toDataURL('image/png').split(',')[1]")gap_x = get_gap_position(image_data)except Exception as e:print(f"缺口檢測失敗,使用默認偏移量: {e}")gap_x = 50 # 備用值# 模擬拖動slider = page.query_selector("#circle")slider_bbox = slider.bounding_box()start_x = slider_bbox["x"] + slider_bbox["width"] / 2start_y = slider_bbox["y"] + slider_bbox["height"] / 2target_x = start_x + gap_x + 8page.mouse.move(start_x, start_y)page.mouse.down()steps = random.randint(15, 30)for i in range(steps):x = start_x + (target_x - start_x) * (i / steps)y = start_y + random.uniform(-2, 2)page.mouse.move(x, y)#time.sleep(random.uniform(0.01, 0.02))page.mouse.up()# 檢查成功if page.query_selector(".popup-success"):print("驗證成功!")return Trueelse:retry_count += 1print(f"驗證失敗,第 {retry_count} 次重試...")time.sleep(0.5)except Exception as e:print(f"滑動過程中出錯: {e}")retry_count += 1return Falsedef login_with_slide(username: str,password: str,login_url: str = "http://test.com/",max_slide_retries: int = 3
) -> dict:"""執行帶滑塊驗證的登錄,并返回登錄接口的 JSON 數據"""with sync_playwright() as playwright:browser = playwright.webkit.launch(headless=True)context = browser.new_context()page = context.new_page()page.set_default_timeout(5000)login_response = Nonedef handle_response(response):nonlocal login_responseif "login2" in response.url and response.status == 200:try:print("登錄接口響應:", response.json())login_response = login_result(response.json())except ValueError:return error_result("接口返回非json數據")page.on("response", handle_response)try:page.goto(login_url)page.get_by_role("textbox", name="請輸入統一社會信用代碼或身份證號").fill(username)page.get_by_role("textbox", name="請輸入密碼").fill(password)page.get_by_role("button", name="登錄").click()if not perform_slide(page, max_slide_retries):return error_result("滑塊驗證失敗")# 等待登錄接口響應page.wait_for_timeout(500) # 簡單延遲,確保響應已捕獲return login_response or error_result("未獲取到登錄異常")except Exception as e:return error_result("f登錄異常: {}"+str(e))finally:page.close()context.close()browser.close()def login_result(response: dict) -> dict:if response.get("result")==0:return success_result("成功",response.get("data",{}).get("userDetail"))return error_result("登錄失敗,接口信息異常")
if __name__ == "__main__":result = login_with_slide(username="admin",password="hpg123789")print("最終結果:", result)