Base.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. from selenium.webdriver.common.by import By
  2. from selenium.webdriver.support.ui import WebDriverWait
  3. from selenium.webdriver.support import expected_conditions as EC
  4. from selenium.webdriver.common.action_chains import ActionChains
  5. from selenium.webdriver.common.keys import Keys
  6. from selenium.common.exceptions import TimeoutException, NoSuchElementException, ElementNotVisibleException
  7. import time
  8. class SeleniumBase:
  9. def __init__(self, driver):
  10. """
  11. 初始化Selenium基础操作类
  12. :param driver: WebDriver实例
  13. """
  14. self.driver = driver
  15. self.wait = WebDriverWait(self.driver, 10) # 默认等待10秒
  16. def set_implicit_wait(self, seconds):
  17. """设置隐式等待时间"""
  18. self.driver.implicitly_wait(seconds)
  19. def set_explicit_wait(self, seconds):
  20. """设置显式等待时间"""
  21. self.wait = WebDriverWait(self.driver, seconds)
  22. # ==================== 浏览器操作 ====================
  23. def open_url(self, url):
  24. """打开指定URL"""
  25. self.driver.get(url)
  26. def get_current_url(self):
  27. """获取当前URL"""
  28. return self.driver.current_url
  29. def get_page_title(self):
  30. """获取页面标题"""
  31. return self.driver.title
  32. def refresh_page(self):
  33. """刷新页面"""
  34. self.driver.refresh()
  35. def navigate_forward(self):
  36. """浏览器前进"""
  37. self.driver.forward()
  38. def navigate_back(self):
  39. """浏览器后退"""
  40. self.driver.back()
  41. def maximize_window(self):
  42. """最大化窗口"""
  43. self.driver.maximize_window()
  44. def set_window_size(self, width, height):
  45. """设置窗口大小"""
  46. self.driver.set_window_size(width, height)
  47. def get_window_size(self):
  48. """获取窗口大小"""
  49. return self.driver.get_window_size()
  50. def switch_to_frame(self, frame_reference):
  51. """切换到指定frame"""
  52. self.driver.switch_to.frame(frame_reference)
  53. def switch_to_default_content(self):
  54. """切换回默认内容"""
  55. self.driver.switch_to.default_content()
  56. def switch_to_window(self, window_handle):
  57. """切换到指定窗口"""
  58. self.driver.switch_to.window(window_handle)
  59. def switch_to_newest_window(self):
  60. """切换到最新打开的窗口"""
  61. handles = self.driver.window_handles
  62. self.driver.switch_to.window(handles[-1])
  63. return handles[-1]
  64. def close_current_window(self):
  65. """关闭当前窗口"""
  66. self.driver.close()
  67. def get_all_window_handles(self):
  68. """获取所有窗口句柄"""
  69. return self.driver.window_handles
  70. def get_current_window_handle(self):
  71. """获取当前窗口句柄"""
  72. return self.driver.current_window_handle
  73. def execute_script(self, script, *args):
  74. """执行JavaScript脚本"""
  75. return self.driver.execute_script(script, *args)
  76. def scroll_to_element(self, element):
  77. """滚动到指定元素"""
  78. self.execute_script("arguments[0].scrollIntoView();", element)
  79. def scroll_to_bottom(self):
  80. """滚动到页面底部"""
  81. self.execute_script("window.scrollTo(0, document.body.scrollHeight);")
  82. def scroll_to_top(self):
  83. """滚动到页面顶部"""
  84. self.execute_script("window.scrollTo(0, 0);")
  85. def take_screenshot(self, filename):
  86. """截取屏幕截图"""
  87. self.driver.save_screenshot(filename)
  88. # ==================== 元素定位 ====================
  89. def find_element(self, by, value, timeout=None):
  90. """
  91. 查找元素(带等待)
  92. :param by: 定位方式 (By.ID, By.XPATH, By.CSS_SELECTOR, etc.)
  93. :param value: 定位值
  94. :param timeout: 超时时间,默认使用类初始化时的等待时间
  95. :return: WebElement
  96. """
  97. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  98. try:
  99. return wait.until(EC.presence_of_element_located((by, value)))
  100. except TimeoutException:
  101. raise NoSuchElementException(f"元素未找到: {by}={value}")
  102. def find_elements(self, by, value, timeout=None):
  103. """
  104. 查找多个元素(带等待)
  105. :param by: 定位方式
  106. :param value: 定位值
  107. :param timeout: 超时时间
  108. :return: 元素列表
  109. """
  110. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  111. try:
  112. return wait.until(EC.presence_of_all_elements_located((by, value)))
  113. except TimeoutException:
  114. return []
  115. def find_visible_element(self, by, value, timeout=None):
  116. """
  117. 查找可见元素
  118. :param by: 定位方式
  119. :param value: 定位值
  120. :param timeout: 超时时间
  121. :return: WebElement
  122. """
  123. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  124. try:
  125. return wait.until(EC.visibility_of_element_located((by, value)))
  126. except TimeoutException:
  127. raise ElementNotVisibleException(f"元素不可见: {by}={value}")
  128. def find_clickable_element(self, by, value, timeout=None):
  129. """
  130. 查找可点击元素
  131. :param by: 定位方式
  132. :param value: 定位值
  133. :param timeout: 超时时间
  134. :return: WebElement
  135. """
  136. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  137. try:
  138. return wait.until(EC.element_to_be_clickable((by, value)))
  139. except TimeoutException:
  140. raise ElementNotVisibleException(f"元素不可点击: {by}={value}")
  141. # 快捷定位方法
  142. def by_id(self, id_value, timeout=None):
  143. """通过ID定位元素"""
  144. return self.find_element(By.ID, id_value, timeout)
  145. def by_name(self, name_value, timeout=None):
  146. """通过Name定位元素"""
  147. return self.find_element(By.NAME, name_value, timeout)
  148. def by_xpath(self, xpath_value, timeout=None):
  149. """通过XPath定位元素"""
  150. return self.find_element(By.XPATH, xpath_value, timeout)
  151. def by_css(self, css_value, timeout=None):
  152. """通过CSS选择器定位元素"""
  153. return self.find_element(By.CSS_SELECTOR, css_value, timeout)
  154. def by_class(self, class_value, timeout=None):
  155. """通过Class定位元素"""
  156. return self.find_element(By.CLASS_NAME, class_value, timeout)
  157. def by_link_text(self, link_text, timeout=None):
  158. """通过链接文本定位元素"""
  159. return self.find_element(By.LINK_TEXT, link_text, timeout)
  160. def by_partial_link_text(self, partial_link_text, timeout=None):
  161. """通过部分链接文本定位元素"""
  162. return self.find_element(By.PARTIAL_LINK_TEXT, partial_link_text, timeout)
  163. def by_tag(self, tag_name, timeout=None):
  164. """通过标签名定位元素"""
  165. return self.find_element(By.TAG_NAME, tag_name, timeout)
  166. # ==================== 元素操作 ====================
  167. def click(self, by, value, timeout=None):
  168. """点击元素"""
  169. element = self.find_clickable_element(by, value, timeout)
  170. element.click()
  171. def input_text(self, by, value, text, timeout=None):
  172. """输入文本"""
  173. element = self.find_element(by, value, timeout)
  174. element.clear()
  175. element.send_keys(text)
  176. def clear_text(self, by, value, timeout=None):
  177. """清除文本"""
  178. element = self.find_element(by, value, timeout)
  179. element.clear()
  180. def get_text(self, by, value, timeout=None):
  181. """获取元素文本"""
  182. element = self.find_element(by, value, timeout)
  183. return element.text
  184. def get_attribute(self, by, value, attribute_name, timeout=None):
  185. """获取元素属性"""
  186. element = self.find_element(by, value, timeout)
  187. return element.get_attribute(attribute_name)
  188. def is_displayed(self, by, value, timeout=None):
  189. """检查元素是否显示"""
  190. try:
  191. element = self.find_visible_element(by, value, timeout)
  192. return element.is_displayed()
  193. except (NoSuchElementException, ElementNotVisibleException):
  194. return False
  195. def is_enabled(self, by, value, timeout=None):
  196. """检查元素是否启用"""
  197. try:
  198. element = self.find_element(by, value, timeout)
  199. return element.is_enabled()
  200. except NoSuchElementException:
  201. return False
  202. def is_selected(self, by, value, timeout=None):
  203. """检查元素是否被选中"""
  204. try:
  205. element = self.find_element(by, value, timeout)
  206. return element.is_selected()
  207. except NoSuchElementException:
  208. return False
  209. def select_dropdown_by_value(self, by, value, option_value, timeout=None):
  210. """通过值选择下拉选项"""
  211. from selenium.webdriver.support.ui import Select
  212. element = self.find_element(by, value, timeout)
  213. select = Select(element)
  214. select.select_by_value(option_value)
  215. def select_dropdown_by_text(self, by, value, option_text, timeout=None):
  216. """通过文本选择下拉选项"""
  217. from selenium.webdriver.support.ui import Select
  218. element = self.find_element(by, value, timeout)
  219. select = Select(element)
  220. select.select_by_visible_text(option_text)
  221. def select_dropdown_by_index(self, by, value, index, timeout=None):
  222. """通过索引选择下拉选项"""
  223. from selenium.webdriver.support.ui import Select
  224. element = self.find_element(by, value, timeout)
  225. select = Select(element)
  226. select.select_by_index(index)
  227. def get_dropdown_options(self, by, value, timeout=None):
  228. """获取下拉选项"""
  229. from selenium.webdriver.support.ui import Select
  230. element = self.find_element(by, value, timeout)
  231. select = Select(element)
  232. return [option.text for option in select.options]
  233. def hover(self, by, value, timeout=None):
  234. """鼠标悬停"""
  235. element = self.find_element(by, value, timeout)
  236. ActionChains(self.driver).move_to_element(element).perform()
  237. def double_click(self, by, value, timeout=None):
  238. """双击元素"""
  239. element = self.find_element(by, value, timeout)
  240. ActionChains(self.driver).double_click(element).perform()
  241. def right_click(self, by, value, timeout=None):
  242. """右键点击元素"""
  243. element = self.find_element(by, value, timeout)
  244. ActionChains(self.driver).context_click(element).perform()
  245. def drag_and_drop(self, source_by, source_value, target_by, target_value, timeout=None):
  246. """拖放元素"""
  247. source_element = self.find_element(source_by, source_value, timeout)
  248. target_element = self.find_element(target_by, target_value, timeout)
  249. ActionChains(self.driver).drag_and_drop(source_element, target_element).perform()
  250. def press_key(self, by, value, key, timeout=None):
  251. """按下键盘按键"""
  252. element = self.find_element(by, value, timeout)
  253. element.send_keys(key)
  254. def press_enter(self, by, value, timeout=None):
  255. """按下回车键"""
  256. self.press_key(by, value, Keys.ENTER, timeout)
  257. def press_tab(self, by, value, timeout=None):
  258. """按下Tab键"""
  259. self.press_key(by, value, Keys.TAB, timeout)
  260. def press_escape(self, by, value, timeout=None):
  261. """按下Esc键"""
  262. self.press_key(by, value, Keys.ESCAPE, timeout)
  263. # ==================== 等待方法 ====================
  264. def wait_for_element_present(self, by, value, timeout=None):
  265. """等待元素出现"""
  266. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  267. return wait.until(EC.presence_of_element_located((by, value)))
  268. def wait_for_element_visible(self, by, value, timeout=None):
  269. """等待元素可见"""
  270. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  271. return wait.until(EC.visibility_of_element_located((by, value)))
  272. def wait_for_element_invisible(self, by, value, timeout=None):
  273. """等待元素不可见"""
  274. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  275. return wait.until(EC.invisibility_of_element_located((by, value)))
  276. def wait_for_element_clickable(self, by, value, timeout=None):
  277. """等待元素可点击"""
  278. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  279. return wait.until(EC.element_to_be_clickable((by, value)))
  280. def wait_for_text_present(self, by, value, text, timeout=None):
  281. """等待元素包含特定文本"""
  282. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  283. return wait.until(EC.text_to_be_present_in_element((by, value), text))
  284. def wait_for_title_contains(self, text, timeout=None):
  285. """等待标题包含特定文本"""
  286. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  287. return wait.until(EC.title_contains(text))
  288. def wait_for_title_is(self, text, timeout=None):
  289. """等待标题等于特定文本"""
  290. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  291. return wait.until(EC.title_is(text))
  292. def wait_for_alert_present(self, timeout=None):
  293. """等待警告框出现"""
  294. wait = self.wait if timeout is None else WebDriverWait(self.driver, timeout)
  295. return wait.until(EC.alert_is_present())
  296. # ==================== 弹窗处理 ====================
  297. def accept_alert(self, timeout=None):
  298. """接受警告框"""
  299. alert = self.wait_for_alert_present(timeout)
  300. alert.accept()
  301. def dismiss_alert(self, timeout=None):
  302. """取消警告框"""
  303. alert = self.wait_for_alert_present(timeout)
  304. alert.dismiss()
  305. def get_alert_text(self, timeout=None):
  306. """获取警告框文本"""
  307. alert = self.wait_for_alert_present(timeout)
  308. return alert.text
  309. def input_alert_text(self, text, timeout=None):
  310. """向警告框输入文本"""
  311. alert = self.wait_for_alert_present(timeout)
  312. alert.send_keys(text)
  313. alert.accept()
  314. # ==================== Cookie操作 ====================
  315. def get_all_cookies(self):
  316. """获取所有Cookie"""
  317. return self.driver.get_cookies()
  318. def get_cookie_by_name(self, name):
  319. """根据名称获取Cookie"""
  320. return self.driver.get_cookie(name)
  321. def add_cookie(self, cookie_dict):
  322. """添加Cookie"""
  323. self.driver.add_cookie(cookie_dict)
  324. def delete_cookie(self, name):
  325. """删除指定Cookie"""
  326. self.driver.delete_cookie(name)
  327. def delete_all_cookies(self):
  328. """删除所有Cookie"""
  329. self.driver.delete_all_cookies()
  330. # ==================== JS执行 ====================
  331. def js_click(self, by, value, timeout=None):
  332. """通过JS点击元素"""
  333. element = self.find_element(by, value, timeout)
  334. self.execute_script("arguments[0].click();", element)
  335. def js_input_text(self, by, value, text, timeout=None):
  336. """通过JS输入文本"""
  337. element = self.find_element(by, value, timeout)
  338. self.execute_script("arguments[0].value = arguments[1];", element, text)
  339. def js_set_attribute(self, by, value, attr_name, attr_value, timeout=None):
  340. """通过JS设置元素属性"""
  341. element = self.find_element(by, value, timeout)
  342. self.execute_script(f"arguments[0].setAttribute('{attr_name}', '{attr_value}');", element)
  343. def js_remove_attribute(self, by, value, attr_name, timeout=None):
  344. """通过JS移除元素属性"""
  345. element = self.find_element(by, value, timeout)
  346. self.execute_script(f"arguments[0].removeAttribute('{attr_name}');", element)
  347. def js_scroll_to_element(self, by, value, timeout=None):
  348. """通过JS滚动到元素"""
  349. element = self.find_element(by, value, timeout)
  350. self.execute_script("arguments[0].scrollIntoView(true);", element)
  351. # ==================== 其他实用方法 ====================
  352. def switch_to_active_element(self):
  353. """切换到当前活动元素"""
  354. return self.driver.switch_to.active_element
  355. def get_page_source(self):
  356. """获取页面源码"""
  357. return self.driver.page_source
  358. def wait_for_page_load(self, timeout=30):
  359. """等待页面完全加载"""
  360. old_page = self.driver.find_element(By.TAG_NAME, 'html')
  361. yield
  362. WebDriverWait(self.driver, timeout).until(
  363. EC.staleness_of(old_page)
  364. )
  365. def is_alert_present(self, timeout=5):
  366. """检查警告框是否存在"""
  367. try:
  368. WebDriverWait(self.driver, timeout).until(EC.alert_is_present())
  369. return True
  370. except TimeoutException:
  371. return False
  372. def wait_for_ajax_complete(self, timeout=30):
  373. """等待AJAX请求完成"""
  374. js_condition = "return jQuery.active == 0"
  375. WebDriverWait(self.driver, timeout).until(
  376. lambda driver: self.execute_script(js_condition)
  377. )
  378. def highlight_element(self, by, value, timeout=None, duration=3):
  379. """高亮显示元素(用于调试)"""
  380. element = self.find_element(by, value, timeout)
  381. original_style = element.get_attribute("style")
  382. self.execute_script("arguments[0].setAttribute('style', arguments[1]);",
  383. element, "border: 2px solid red; border-style: dashed;")
  384. time.sleep(duration)
  385. self.execute_script("arguments[0].setAttribute('style', arguments[1]);",
  386. element, original_style)