淺嘗Selenium自動化框架
- Selenium基本介紹
- Selenium原理
- Selenium學習要點
- 寫個Selenium Demo
- 結束
Selenium基本介紹
Selenium 是一個開源的自動化測試工具,只用于測試 Web 應用程序。它支持多種編程語言(如 Java、Python、C# 等)來編寫測試腳本,同時兼容主流的瀏覽器(如 Chrome、Firefox、Edge 等)。
👉👉👉官網 官方文檔
直白點一句話總結:Selenium用來做桌面端(win/mac)上的瀏覽器web自動化。
Selenium原理
+-------------------------------------+
| Test Script (Client) |
| (Python, Java, C#, etc.) |
+-------------------+-----------------+|WebDriver API (JSON Wire Protocol)|
+-------------------+-----------------+
| Browser Drivers |
| (ChromeDriver, GeckoDriver, etc.) |
+-------------------+-----------------+|Browsers(Chrome, Firefox, Edge, etc.)
- Test Script
用戶通過編程語言調用 WebDriver API 來編寫測試用例。 - WebDriver API
提供了瀏覽器操作的方法(如 .click(), .get())。
WebDriver 使用 JSON Wire Protocol 與瀏覽器驅動通信,將測試用例中的操作翻譯成瀏覽器能夠理解的命令。 - Browser Drivers
瀏覽器驅動(中間層),將 API 的調用翻譯為瀏覽器可理解的指令。
不同瀏覽器需要不同的驅動程序(如 ChromeDriver、GeckoDriver 等)。
驅動程序將 WebDriver 發出的命令傳遞給瀏覽器。 - Browsers 瀏覽器
瀏覽器根據驅動程序的指令執行實際的操作(如打開頁面、點擊按鈕等),并返回執行結果。
Selenium學習要點
我是基于python學習的,步驟非常簡單。
pip install selenium
然后要學一下XPath這種XML查詢語言,就可以開始寫代碼啦。
寫個Selenium Demo
最近蛀牙了,做根管治療,要經常定期掛號,但是那醫生又有點人氣,放號又是隨機時間,經常約不到號,就很尷尬。所以就想做一個自動通知掛號的腳本。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import random
from typing import Optional, List
from datetime import datetimedef find_doctor_and_nextpage(driver, doctor_name, hospital_name, department_name):page_index = 0while True:page_index += 1doctors = driver.find_elements(By.XPATH, '//*[@id="expert_div_1"]/div[*]/div[1]/div[2]')for doctor in doctors:text = doctor.textif doctor_name in text and hospital_name in text and department_name in text:try:# 在當前醫生元素中查找鏈接link_element = doctor.find_element(By.XPATH, './ul/li[1]/span[1]/a') # 相對 XPathhref = link_element.get_attribute("href") # 獲取鏈接的 href 屬性print(f"Found the link to the doctor at page {page_index}. href={href}")return hrefexcept Exception as e:print(f"Do not find the doctor link, doctor.text={text}. {e}")# 搜索下一頁try:switch_page_bar = driver.find_element(By.XPATH, '//*[@id="page1"]')next_page_btn = switch_page_bar.find_element(By.XPATH, './/*[contains(text(), "下一頁")]')href = next_page_btn.get_attribute('href')if href:print(f"Page {page_index} has been searched completely with no matches found, proceeding to the next page.")time.sleep(random.randint(2, 5))next_page_btn.click()else:print(f"The search has reached the last page {page_index} and no matches found.")return Falseexcept Exception as e:print(f"find_doctor_and_nextpage Error. {e}")return Falsedef search_doctor(driver, base_url, doctor_name, hospital_name, department_name):try:# 打開搜索頁面(基網址傳入)driver.get(base_url)# 定位搜索框并輸入醫生doctor_input = driver.find_element(By.XPATH, "//*[@id='txtSearch']")doctor_input.clear()doctor_input.send_keys(doctor_name)# 點擊搜索按鈕search_button = driver.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div/input[3]")search_button.click()try:# 等待跳轉到結果頁面WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.XPATH, "//*[@id='expert_div_1']/div[1]")))except Exception as e:print(f"Search the doctor timeout, name={doctor_name}. {e}")return Falsedoctor_link = find_doctor_and_nextpage(driver, doctor_name, hospital_name, department_name)# print(f"doctor_link={doctor_link}")return doctor_linkexcept Exception as e:print(f":Search the doctor failed, name={doctor_name}. {e}")return Falsedef convert_to_datetime(date_str):try:return datetime.strptime(date_str, "%m-%d")except ValueError:return Nonedef parse_target_date(target_date) -> List[datetime]:"""解析入參 target_date,支持單個日期、多個日期和日期范圍。'01-05''01-05, 01-07, 01-19''01-05~02-02'返回日期對象列表。"""if isinstance(target_date, str):if '~' in target_date: # 日期范圍start_date_str, end_date_str = target_date.split('~')start_date = convert_to_datetime(start_date_str)end_date = convert_to_datetime(end_date_str)return [start_date, end_date] if start_date and end_date else []elif ',' in target_date: # 多個日期date_str_list = [date.strip() for date in target_date.split(',')]converted_dates = [convert_to_datetime(date_str) for date_str in date_str_list]return [date for date in converted_dates if date]else: # 單個日期single_date = convert_to_datetime(target_date)return [single_date] if single_date else []return []def check_availability(driver, doctor_link, target_date, target_time: Optional[str]):"""在網頁中檢查是否有目標日期時間的號源"""can_be_appointments = Falsetry:driver.get(doctor_link)try:# 等待跳轉到結果頁面doctor_subscribe = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, '//*[@class="doctorsubscribe"]')))except Exception as e:print(f"Open link {doctor_link} timeout. {e}")return False# 匹配日期date_items = doctor_subscribe.find_elements(By.XPATH, './div[1]/ul/li/span[1]')parsed_dates = parse_target_date(target_date.strip())matched_indices = []if '~' in target_date: # 日期范圍if len(parsed_dates) == 2 and isinstance(parsed_dates[0], datetime) and isinstance(parsed_dates[1],datetime): # 日期范圍start_date, end_date = parsed_datesfor idx, item in enumerate(date_items):item_date = convert_to_datetime(item.text)if item_date and start_date <= item_date <= end_date:matched_indices.append(idx)elif len(parsed_dates) > 1: # 多個日期for idx, item in enumerate(date_items):item_date = convert_to_datetime(item.text)if item_date and item.text in [date.strftime("%m-%d") for date in parsed_dates]:matched_indices.append(idx)elif len(parsed_dates) == 1: # 單個日期for idx, item in enumerate(date_items):item_date = convert_to_datetime(item.text)if item_date and item.text == parsed_dates[0].strftime("%m-%d"):matched_indices.append(idx)if len(matched_indices) > 0:print(f"Found the matched date index {matched_indices}")else:print(f"No matched date can be found.")return Falsewhliesubscribe = doctor_subscribe.find_elements(By.XPATH, './div[2]/ul/li')for index in matched_indices:if index < len(whliesubscribe):element = whliesubscribe[index]if "預約" in element.text:can_be_appointments = Trueprint(f"There are available slots on {date_items[index].text}, and appointments can be made.")return True# 匹配時間print(f"can_be_appointments={can_be_appointments}")except Exception as e:print(f"check_availability Error. {e}")return Falsedef book_appointment(driver):"""執行掛號流程"""try:# 點擊預約按鈕book_button = driver.find_element(By.ID, "book_button") # 替換為實際 IDbook_button.click()# 填寫必要的預約信息patient_name = driver.find_element(By.ID, "patient_name_input") # 替換為實際 IDpatient_phone = driver.find_element(By.ID, "patient_phone_input") # 替換為實際 IDpatient_name.send_keys("測試患者") # 替換為實際患者姓名patient_phone.send_keys("12345678901") # 替換為實際聯系電話# 確認預約confirm_button = driver.find_element(By.ID, "confirm_button") # 替換為實際 IDconfirm_button.click()print("掛號成功!")except Exception as e:print(f"掛號出錯: {e}")def main(base_url, doctor_name, hospital, department, target_date, target_time):"""主程序入口"""driver = webdriver.Chrome() # 瀏覽器驅動try:# 步驟 1:搜索醫生doctor_link = search_doctor(driver, base_url, doctor_name, hospital, department)if not doctor_link:print("Search doctor failed.")return# doctor_link = 'https://www.xxx.com/UrpOnline/Home/Doctor/2439FC00A213861E30C599CDDD0833B8'# 步驟 2:檢查是否有目標日期的號源if check_availability(driver, doctor_link, target_date, target_time):print(f"找到 {target_date} 的號源,開始預約...")else:print(f"沒有找到 {target_date} 的號源。")finally:# 關閉瀏覽器driver.quit()# 示例調用
if __name__ == "__main__":main(base_url="https://www.xxx.com/UrpOnline/Home/DoctorList/",doctor_name="醫師",# doctor_name="張三",hospital="中山醫院",department="皮膚科",# department="便民門診2",# department="消化內科asdf午間門診", # 第4頁# target_date="01-05~01-06",target_date="01-05~02-01",target_time=None)
結束
有什么問題可以留言或者私信哦!