1
1
mirror of https://github.com/KenanZhu/AutoLibrary.git synced 2026-06-17 23:13:03 +08:00

fix(pages): 移除裸 except Exception 改用精确异常类型并加固元素操作防护

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 01:36:18 +08:00
parent 910e3e3224
commit df7ad92f7f
13 changed files with 156 additions and 195 deletions
-3
View File
@@ -149,9 +149,6 @@ class AutoLib(MsgBase):
except WebDriverException as e:
self._showTrace(f"浏览器驱动初始化失败: {e}", self.TraceLevel.ERROR)
return False
except Exception as e:
self._showTrace(f"浏览器驱动初始化失败: {e}", self.TraceLevel.ERROR)
return False
self._showTrace(f"浏览器驱动已初始化, 类型: {self.__driver_type}, 路径: {self.__driver_path}")
return True
+15 -23
View File
@@ -85,9 +85,7 @@ class LoginPage:
EC.presence_of_element_located(self.CAPTCHA_IMG)
)
return True
except (NoSuchElementException, TimeoutException):
return False
except Exception:
except TimeoutException:
return False
def fillCredentials(
@@ -104,17 +102,21 @@ class LoginPage:
el.clear()
el.send_keys(password)
return True
except (NoSuchElementException, TimeoutException):
return False
except Exception:
except (NoSuchElementException, ElementNotInteractableException):
return False
def getCaptchaImageSrc(
self,
) -> str:
) -> str | None:
captcha_el = self._driver.find_element(*self.CAPTCHA_IMG)
return captcha_el.get_attribute("src")
# return 'None' if captcha image element is not found.
# But the 'get_attribute("src")' also return 'None' if there's no attribute with
# that name, which is not what we want.
try:
captcha_el = self._driver.find_element(*self.CAPTCHA_IMG)
return captcha_el.get_attribute("src")
except NoSuchElementException:
return None
def refreshCaptcha(
self,
@@ -123,10 +125,7 @@ class LoginPage:
try:
self._driver.find_element(*self.CAPTCHA_IMG).click()
return True
except (NoSuchElementException, TimeoutException,
ElementNotInteractableException):
return False
except Exception:
except (NoSuchElementException, ElementNotInteractableException):
return False
def fillCaptcha(
@@ -139,9 +138,7 @@ class LoginPage:
el.clear()
el.send_keys(captcha_text)
return True
except (NoSuchElementException, TimeoutException):
return False
except Exception:
except (NoSuchElementException, ElementNotInteractableException):
return False
def clickLogin(
@@ -151,10 +148,7 @@ class LoginPage:
try:
self._driver.find_element(*self.LOGIN_BUTTON).click()
return True
except (NoSuchElementException, TimeoutException,
ElementNotInteractableException):
return False
except Exception:
except (NoSuchElementException, ElementNotInteractableException):
return False
def waitLoginSuccess(
@@ -172,9 +166,7 @@ class LoginPage:
EC.presence_of_element_located(self.SUCCESS_INDICATOR_CONTENT)
)
return True
except (NoSuchElementException, TimeoutException):
return False
except Exception:
except TimeoutException:
return False
def stopPageLoad(
+40 -29
View File
@@ -14,8 +14,10 @@ from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.common.exceptions import (
ElementNotInteractableException,
NoSuchElementException,
TimeoutException,
WebDriverException,
)
from pages.ReserveView import ReserveView
@@ -38,6 +40,15 @@ class MainShell:
self._driver = driver
def _clickTab(
self,
locator: tuple,
) -> None:
WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(locator)
).click()
def gotoReserveView(
self,
) -> ReserveView:
@@ -65,9 +76,7 @@ class MainShell:
try:
self._driver.find_element(*self.TAB_LOGOUT).click()
return True
except NoSuchElementException:
return False
except Exception:
except (NoSuchElementException, ElementNotInteractableException):
return False
def waitCheckinButton(
@@ -81,8 +90,6 @@ class MainShell:
return True
except TimeoutException:
return False
except Exception:
return False
def waitExtendButton(
self,
@@ -95,40 +102,50 @@ class MainShell:
return True
except TimeoutException:
return False
except Exception:
return False
def isCheckinButtonDisabled(
self,
) -> bool:
btn = self._driver.find_element(*self.BTN_CHECKIN)
return "disabled" in btn.get_attribute("class")
try:
btn = self._driver.find_element(*self.BTN_CHECKIN)
return "disabled" in btn.get_attribute("class")
except NoSuchElementException:
return True
def isExtendButtonDisabled(
self,
) -> bool:
btn = self._driver.find_element(*self.BTN_EXTEND)
return "disabled" in btn.get_attribute("class")
try:
btn = self._driver.find_element(*self.BTN_EXTEND)
return "disabled" in btn.get_attribute("class")
except NoSuchElementException:
return True
def clickCheckinButton(
self,
) -> None:
btn = WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(self.BTN_CHECKIN)
)
btn.click()
try:
btn = WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(self.BTN_CHECKIN)
)
btn.click()
except (TimeoutException, ElementNotInteractableException):
return
def clickExtendButton(
self,
) -> None:
btn = WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(self.BTN_EXTEND)
)
btn.click()
try:
btn = WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(self.BTN_EXTEND)
)
btn.click()
except (TimeoutException, ElementNotInteractableException):
return
def enableCheckinButtonByJS(
self,
@@ -154,13 +171,7 @@ class MainShell:
self,
) -> None:
self._driver.refresh()
def _clickTab(
self,
locator: tuple,
) -> None:
WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(locator)
).click()
try:
self._driver.refresh()
except (TimeoutException, WebDriverException):
return
-6
View File
@@ -44,8 +44,6 @@ class RecordsView:
return self._driver.find_elements(*self.RECORDS_LIST)
except TimeoutException:
return None
except Exception:
return None
def getRecordTimeElement(
self,
@@ -71,8 +69,6 @@ class RecordsView:
)
except TimeoutException:
return False
except Exception:
return False
try:
more_btn = self._driver.find_element(*self.MORE_BTN)
if more_btn.is_displayed() and more_btn.is_enabled():
@@ -82,8 +78,6 @@ class RecordsView:
return False
except (NoSuchElementException, StaleElementReferenceException):
return False
except Exception:
return False
def getRecordText(
self,
+48 -55
View File
@@ -53,6 +53,50 @@ class ReserveView:
self._driver = driver
def _clickOptionByJS(
self,
trigger_id: str,
option_css: str,
) -> bool:
script = f"""
try {{
var trigger = document.getElementById('{trigger_id}');
if (trigger) {{
trigger.click();
var option = document.querySelector("{option_css}");
if (option) {{
option.click();
return true;
}}
return false;
}}
return false;
}} catch (e) {{
return false;
}}
"""
result = self._driver.execute_script(script)
time.sleep(0.1)
return result
def _clickOption(
self,
trigger: tuple,
option: tuple,
) -> bool:
try:
WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(trigger)
).click()
WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(option)
).click()
return True
except (TimeoutException, ElementNotInteractableException):
return False
def selectDate(
self,
date_str: str,
@@ -109,21 +153,15 @@ class ReserveView:
).click()
except (TimeoutException, ElementNotInteractableException):
return None
except Exception:
return None
try:
WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable((By.ID, self.ROOM_BTN_FMT.format(room=room)))
).click()
except (TimeoutException, ElementNotInteractableException):
return None
except Exception:
return None
try:
return SeatMapDialog(self._driver)
except (TimeoutException):
return None
except Exception:
except TimeoutException:
return None
def submitReserve(
@@ -137,57 +175,12 @@ class ReserveView:
return True
except (TimeoutException, ElementNotInteractableException):
return False
except Exception:
return False
def refresh(
self,
) -> None:
self._driver.refresh()
def _clickOptionByJS(
self,
trigger_id: str,
option_css: str,
) -> bool:
script = f"""
try {{
var trigger = document.getElementById('{trigger_id}');
if (trigger) {{
trigger.click();
var option = document.querySelector("{option_css}");
if (option) {{
option.click();
return true;
}}
return false;
}}
return false;
}} catch (e) {{
return false;
}}
"""
result = self._driver.execute_script(script)
time.sleep(0.1)
return result
def _clickOption(
self,
trigger: tuple,
option: tuple,
) -> bool:
try:
WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(trigger)
).click()
WebDriverWait(self._driver, 2).until(
EC.element_to_be_clickable(option)
).click()
return True
except (TimeoutException, ElementNotInteractableException):
return False
except Exception:
return False
self._driver.refresh()
except TimeoutException:
return
+3 -8
View File
@@ -45,9 +45,8 @@ class CheckinResultDialog(Dialog):
self._waitPresence(self.RESULT_MSG)
el = self._find(*self.RESULT_MSG)
return el.text
except (TimeoutException, NoSuchElementException, StaleElementReferenceException):
return ""
except Exception:
except (TimeoutException, NoSuchElementException,
StaleElementReferenceException):
return ""
def getDetails(
@@ -59,8 +58,6 @@ class CheckinResultDialog(Dialog):
return [el.text for el in elements if el.text.strip()]
except (NoSuchElementException, StaleElementReferenceException):
return []
except Exception:
return []
def clickOk(
self,
@@ -69,7 +66,5 @@ class CheckinResultDialog(Dialog):
try:
self._waitClickable(self.OK_BTN).click()
return True
except (NoSuchElementException, TimeoutException, ElementNotInteractableException):
return False
except Exception:
except (TimeoutException, ElementNotInteractableException):
return False
+16 -13
View File
@@ -10,6 +10,7 @@ See the LICENSE file for details.
from selenium.common.exceptions import (
ElementNotInteractableException,
NoSuchElementException,
StaleElementReferenceException,
TimeoutException,
)
from selenium.webdriver.common.by import By
@@ -50,9 +51,7 @@ class RenewDialog(Dialog):
self._waitVisible(self.ROOT)
self._waitPresence(self.MESSAGE_HEAD)
self._waitPresence(self.RESULT_MSG)
except (NoSuchElementException, TimeoutException):
return False
except Exception:
except TimeoutException:
return False
head_msg = self._find(*self.MESSAGE_HEAD).text.strip()
if "警告" in head_msg:
@@ -60,9 +59,7 @@ class RenewDialog(Dialog):
try:
self._waitAllPresence(self.TIME_OPTS)
self._waitPresence(self.OK_BTN)
except (NoSuchElementException, TimeoutException):
return False
except Exception:
except TimeoutException:
return False
return True
@@ -70,13 +67,19 @@ class RenewDialog(Dialog):
self,
) -> str:
return self._find(*self.MESSAGE_HEAD).text.strip()
try:
return self._find(*self.MESSAGE_HEAD).text.strip()
except (NoSuchElementException, StaleElementReferenceException):
return ""
def getResultMessage(
self,
) -> str:
return self._find(*self.RESULT_MSG).text.strip()
try:
return self._find(*self.RESULT_MSG).text.strip()
except (NoSuchElementException, StaleElementReferenceException):
return ""
def getTimeOptions(
self,
@@ -101,7 +104,10 @@ class RenewDialog(Dialog):
prefer_earlier,
)
if result.selected_index >= 0:
all_time_opts[result.selected_index].click()
try:
all_time_opts[result.selected_index].click()
except (ElementNotInteractableException, StaleElementReferenceException):
return TimeSelectionResult(free_times=result.free_times)
return result
def getOkButton(
@@ -117,8 +123,5 @@ class RenewDialog(Dialog):
try:
self._find(*self.OK_BTN).click()
return True
except (NoSuchElementException, TimeoutException,
ElementNotInteractableException):
return False
except Exception:
except (NoSuchElementException, ElementNotInteractableException):
return False
@@ -45,8 +45,6 @@ class ReserveResultDialog(Dialog):
return self._find(*self._titleLocator()).text
except (NoSuchElementException, StaleElementReferenceException):
return ""
except Exception:
return ""
def isSuccess(
self,
@@ -77,5 +75,3 @@ class ReserveResultDialog(Dialog):
return [el.text for el in elements if el.text.strip()]
except (NoSuchElementException, StaleElementReferenceException):
return []
except Exception:
return []
+1 -7
View File
@@ -43,9 +43,7 @@ class SeatMapDialog(Dialog):
try:
self._waitAllPresence(self.SEAT_ITEMS)
except (NoSuchElementException, TimeoutException):
return None
except Exception:
except TimeoutException:
return None
try:
seat_el = self._find(By.ID, f"seat_{int(seat_id):03d}")
@@ -58,8 +56,6 @@ class SeatMapDialog(Dialog):
except (NoSuchElementException, ValueError, TimeoutException,
ElementNotInteractableException, StaleElementReferenceException):
pass
except Exception:
pass
try:
all_seats = self._findAll(*self.SEAT_ITEMS)
seat_id_upper = seat_id.lstrip('0').upper()
@@ -76,5 +72,3 @@ class SeatMapDialog(Dialog):
except (NoSuchElementException, TimeoutException,
ElementNotInteractableException, StaleElementReferenceException):
return None
except Exception:
return None
+7 -5
View File
@@ -13,7 +13,8 @@ import logging
from typing import TYPE_CHECKING, Callable, Optional
from selenium.common.exceptions import (
NoSuchElementException,
ElementNotInteractableException,
StaleElementReferenceException,
TimeoutException,
)
from selenium.webdriver.common.by import By
@@ -106,9 +107,7 @@ class TimeSelectDialog(Dialog):
self._waitAllPresence(
(By.CSS_SELECTOR, f"#{time_id} ul li a")
)
except (NoSuchElementException, TimeoutException):
return []
except Exception:
except TimeoutException:
return []
return self._findAll(
By.CSS_SELECTOR,
@@ -133,7 +132,10 @@ class TimeSelectDialog(Dialog):
prefer_earlier,
)
if result.selected_index >= 0:
all_time_opts[result.selected_index].click()
try:
all_time_opts[result.selected_index].click()
except (ElementNotInteractableException, StaleElementReferenceException):
return TimeSelectionResult(free_times=result.free_times)
return result
def selectTimeRange(
+2 -4
View File
@@ -10,6 +10,7 @@ See the LICENSE file for details.
import queue
from selenium.common.exceptions import (
ElementNotInteractableException,
NoSuchElementException,
TimeoutException,
)
@@ -83,9 +84,6 @@ class CheckinFlow(MsgBase):
dialog.clickOk()
self._showTrace(f"用户 {username} 签到失败 !", self.TraceLevel.ERROR)
return False
except (NoSuchElementException, TimeoutException):
self._showTrace("签到时发生未知错误 !", self.TraceLevel.ERROR)
return False
except Exception:
except (TimeoutException, NoSuchElementException, ElementNotInteractableException):
self._showTrace("签到时发生未知错误 !", self.TraceLevel.ERROR)
return False
+23 -31
View File
@@ -39,6 +39,28 @@ class RenewFlow(MsgBase):
self._driver: WebDriver = driver
self._shell: MainShell = shell
def _validateRenewTime(
self,
end_time: str,
target_renew_mins: int,
) -> bool:
if target_renew_mins > self.LIBRARY_CLOSE_MINS:
actual_renew_duration = self.LIBRARY_CLOSE_MINS - timeStrToMins(end_time)
if actual_renew_duration <= 0:
self._showTrace(
f"当前结束时间 {end_time} 已接近闭馆时间,无法续约 !", self.TraceLevel.ERROR
)
return False
self._showTrace(
f"续约时间已调整至闭馆时间 "
f"{minsToTimeStr(self.LIBRARY_CLOSE_MINS)},"
f"实际续约时长为 "
f"{actual_renew_duration // 60} 小时 "
f"{actual_renew_duration % 60} 分钟"
)
return True
def execute(
self,
username: str,
@@ -106,37 +128,7 @@ class RenewFlow(MsgBase):
self._showTrace(f"当前可供续约的时间有: {result.free_times}")
self._shell.refresh()
return False
except (NoSuchElementException, TimeoutException) as e:
except (NoSuchElementException, TimeoutException, ElementNotInteractableException) as e:
self._showTrace(f"用户 {username} 续约失败 ! : {e}", self.TraceLevel.ERROR)
self._shell.refresh()
return False
except (ElementNotInteractableException) as e:
self._showTrace(f"用户 {username} 续约失败 ! : {e}", self.TraceLevel.ERROR)
self._shell.refresh()
return False
except Exception as e:
self._showTrace(f"用户 {username} 续约失败 ! : {e}", self.TraceLevel.ERROR)
self._shell.refresh()
return False
def _validateRenewTime(
self,
end_time: str,
target_renew_mins: int,
) -> bool:
if target_renew_mins > self.LIBRARY_CLOSE_MINS:
actual_renew_duration = self.LIBRARY_CLOSE_MINS - timeStrToMins(end_time)
if actual_renew_duration <= 0:
self._showTrace(
f"当前结束时间 {end_time} 已接近闭馆时间,无法续约 !", self.TraceLevel.ERROR
)
return False
self._showTrace(
f"续约时间已调整至闭馆时间 "
f"{minsToTimeStr(self.LIBRARY_CLOSE_MINS)},"
f"实际续约时长为 "
f"{actual_renew_duration // 60} 小时 "
f"{actual_renew_duration % 60} 分钟"
)
return True
+1 -7
View File
@@ -12,7 +12,6 @@ from dataclasses import dataclass
from selenium.common.exceptions import (
ElementNotInteractableException,
NoSuchElementException,
TimeoutException,
)
from selenium.webdriver.remote.webdriver import WebDriver
@@ -70,10 +69,7 @@ class ReserveFlow(MsgBase):
try:
view = self._shell.gotoReserveView()
except (NoSuchElementException, TimeoutException) as e:
self._showTrace(f"加载预约选座页面失败 ! : {e}", self.TraceLevel.ERROR)
return False
except Exception as e:
except (TimeoutException, ElementNotInteractableException) as e:
self._showTrace(f"加载预约选座页面失败 ! : {e}", self.TraceLevel.ERROR)
return False
if not view.selectDate(ctx.date):
@@ -140,8 +136,6 @@ class ReserveFlow(MsgBase):
self._showTrace("预约结果加载失败 !", self.TraceLevel.ERROR)
except (TimeoutException, ElementNotInteractableException):
self._showTrace("预约提交失败 !", self.TraceLevel.ERROR)
except Exception:
self._showTrace("预约提交失败 !", self.TraceLevel.ERROR)
if not submit_reserve and have_hover_on_page:
view.refresh()
if reserve_success: