文章目录
-
- # 使用pip安装最新版本 pip install selenium # 安装特定版本 pip install selenium==4.10.0 # 在虚拟环境中安装(推荐) python -m venv selenium_env source selenium_env/bin/activate # Linux/Mac selenium_envScriptsactivate.bat # Windows pip install selenium
- 从Selenium 4.0开始,提供了自动管理驱动的功能,但了解手动安装方法仍然很重要: Chrome浏览器: 访问 ChromeDriver 下载页面 下载与本地Chrome版本匹配的驱动程序 将驱动添加到系统PATH中或在代码中指定路径 Firefox浏览器: 访问 GeckoDriver 下载页面 下载适用于你操作系统的版本 将驱动添加到系统PATH中或在代码中指定路径 Edge浏览器: 访问 Microsoft Edge Driver 下载页面 下载与本地Edge版本匹配的驱动程序 Safari浏览器: Safari驱动已内置于macOS中 需要在Safari浏览器中启用开发者模式
- Selenium 4.x引入了许多重要的改进和新功能: 相对定位:允许基于其他元素的位置来查找元素 Service对象:用于更好地管理驱动程序服务 WebDriver Manager:自动管理驱动程序的下载和设置 CDP(Chrome DevTools Protocol)支持:允许访问浏览器特定的功能
-
- from selenium import webdriver from selenium.webdriver.chrome.service import Service # 自动管理驱动 driver = webdriver.Chrome()
- from selenium import webdriver from selenium.webdriver.chrome.service import Service # 指定驱动路径 service = Service(executable_path=’/path/to/chromedriver’) driver = webdriver.Chrome(service=service)
- from selenium import webdriver from selenium.webdriver.chrome.options import Options # 创建Chrome选项对象 chrome_options = Options() chrome_options.add_argument(“–headless”) # 无头模式 chrome_options.add_argument(“–window-size=1920,1080”) # 设置窗口大小 chrome_options.add_argument(“–disable-gpu”) # 禁用GPU加速 chrome_options.add_argument(“–disable-extensions”) # 禁用扩展 chrome_options.add_argument(“–proxy-server=’direct://'”) # 代理设置 chrome_options.add_argument(“–proxy-bypass-list=*”) # 绕过代理 chrome_options.add_argument(“–start-maximized”) # 启动时最大化窗口 chrome_options.add_experimental_option(“prefs”, { “download.default_directory”: “/path/to/download/directory”, # 设置下载目录 “download.prompt_for_download”: False, # 禁用下载提示 “download.directory_upgrade”: True, “safebrowsing.enabled”: True }) # 初始化WebDriver driver = webdriver.Chrome(options=chrome_options)
- from selenium import webdriver # 初始化WebDriver driver = webdriver.Chrome() # 窗口操作 driver.maximize_window() # 最大化窗口 driver.set_window_size(1920, 1080) # 设置窗口大小 driver.set_window_position(0, 0) # 设置窗口位置 # 导航操作 driver.get(‘https://www.example.com’) # 打开URL driver.back() # 后退 driver.forward() # 前进 driver.refresh() # 刷新页面 # 页面信息 title = driver.title # 获取页面标题 url = driver.current_url # 获取当前URL page_source = driver.page_source # 获取页面源代码 # Cookie操作 driver.add_cookie({“name”: “key”, “value”: “value”}) # 添加Cookie cookies = driver.get_cookies() # 获取所有Cookies driver.delete_cookie(“key”) # 删除特定Cookie driver.delete_all_cookies() # 删除所有Cookies # 关闭操作 driver.close() # 关闭当前标签页 driver.quit() # 关闭浏览器,释放资源
-
- from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument(“–headless”) driver = webdriver.Chrome(options=chrome_options)
- from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument(‘–proxy-server=http://proxyserver:port’) driver = webdriver.Chrome(options=chrome_options)
- from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = Options() prefs = {“profile.managed_default_content_settings.images”: 2} chrome_options.add_experimental_option(“prefs”, prefs) driver = webdriver.Chrome(options=chrome_options)
- Selenium提供了多种定位元素的方法,每种都有其适用场景: from selenium import webdriver from selenium.webdriver.common.by import By driver = webdriver.Chrome() driver.get(“https://www.example.com”) # 1. 通过ID定位(最推荐,高效且唯一) element = driver.find_element(By.ID, “login-button”) # 2. 通过Name属性定位 element = driver.find_element(By.NAME, “username”) # 3. 通过Class Name定位(不唯一时返回第一个匹配元素) element = driver.find_element(By.CLASS_NAME, “login-form”) # 4. 通过Tag Name定位 element = driver.find_element(By.TAG_NAME, “button”) # 5. 通过Link Text定位(完全匹配) element = driver.find_element(By.LINK_TEXT, “Forgot Password?”) # 6. 通过Partial Link Text定位(部分匹配) element = driver.find_element(By.PARTIAL_LINK_TEXT, “Forgot”) # 7. 通过CSS选择器定位(强大且灵活) element = driver.find_element(By.CSS_SELECTOR, “#login-form .submit-button”) # 8. 通过XPath定位(最强大但可能较慢) element = driver.find_element(By.XPATH, “//div[@id=’login-form’]//button”)
-
- # 绝对路径(从根节点开始) element = driver.find_element(By.XPATH, “/html/body/div/form/input”) # 相对路径(从任意节点开始) element = driver.find_element(By.XPATH, “//input[@name=’username’]”) # 使用contains()函数 element = driver.find_element(By.XPATH, “//button[contains(@class, ‘login’)]”) # 使用text()函数 element = driver.find_element(By.XPATH, “//a[text()=’Forgot Password?’]”) element = driver.find_element(By.XPATH, “//a[contains(text(), ‘Forgot’)]”) # 使用AND和OR操作符 element = driver.find_element(By.XPATH, “//input[@type=’text’ and @name=’username’]”) element = driver.find_element(By.XPATH, “//button[@type=’submit’ or @type=’button’]”) # 通过父子关系定位 element = driver.find_element(By.XPATH, “//form[@id=’login-form’]/input”) parent = driver.find_element(By.XPATH, “//input[@id=’username’]/..”) # 通过兄弟关系定位 element = driver.find_element(By.XPATH, “//input[@id=’username’]/following-sibling::input”) element = driver.find_element(By.XPATH, “//input[@id=’password’]/preceding-sibling::input”) # 按索引定位 element = driver.find_element(By.XPATH, “(//input[@type=’text’])[2]”) # 使用轴(axes) element = driver.find_element(By.XPATH, “//input[@id=’username’]/ancestor::form”) element = driver.find_element(By.XPATH, “//form/descendant::input”)
- # 基本选择器 element = driver.find_element(By.CSS_SELECTOR, “#login-button”) # ID选择器 element = driver.find_element(By.CSS_SELECTOR, “.login-form”) # Class选择器 element = driver.find_element(By.CSS_SELECTOR, “input”) # 标签选择器 # 属性选择器 element = driver.find_element(By.CSS_SELECTOR, “input[name=’username’]”) element = driver.find_element(By.CSS_SELECTOR, “input[name^=’user’]”) # 以user开头 element = driver.find_element(By.CSS_SELECTOR, “input[name$=’name’]”) # 以name结尾 element = driver.find_element(By.CSS_SELECTOR, “input[name*=’erna’]”) # 包含erna # 组合选择器 element = driver.find_element(By.CSS_SELECTOR, “form input[type=’text’]”) element = driver.find_element(By.CSS_SELECTOR, “form > input”) # 直接子元素 element = driver.find_element(By.CSS_SELECTOR, “label + input”) # 紧邻兄弟元素 element = driver.find_element(By.CSS_SELECTOR, “label ~ input”) # 通用兄弟元素 # 伪类选择器 element = driver.find_element(By.CSS_SELECTOR, “input:first-child”) element = driver.find_element(By.CSS_SELECTOR, “input:last-child”) element = driver.find_element(By.CSS_SELECTOR, “input:nth-child(2)”)
- from selenium.webdriver.support.relative_locator import locate_with # 获取参考元素 username_field = driver.find_element(By.ID, “username”) # 使用相对定位 password_field = driver.find_element(locate_with(By.TAG_NAME, “input”).below(username_field)) login_button = driver.find_element(locate_with(By.TAG_NAME, “button”).below(password_field)) remember_me = driver.find_element(locate_with(By.TAG_NAME, “input”).to_right_of(password_field)) forgot_password = driver.find_element(locate_with(By.TAG_NAME, “a”).above(login_button))
- # 查找所有符合条件的元素 elements = driver.find_elements(By.CSS_SELECTOR, “.product-item”) # 遍历元素列表 for element in elements: name = element.find_element(By.CLASS_NAME, “product-name”).text price = element.find_element(By.CLASS_NAME, “product-price”).text print(f”产品名称: {name}, 价格: {price}”)
- # 输入操作 element.send_keys(“test@example.com”) # 输入文本 element.send_keys(Keys.CONTROL, ‘a’) # 键盘组合键(全选) element.send_keys(Keys.BACK_SPACE) # 退格键 # 点击操作 element.click() # 点击元素 element.submit() # 提交表单(适用于表单元素内) # 清除操作 element.clear() # 清除文本输入框 # 获取元素属性和状态 value = element.get_attribute(“value”) # 获取属性值 text = element.text # 获取元素文本内容 tag = element.tag_name # 获取标签名 size = element.size # 获取元素大小 location = element.location # 获取元素位置 is_enabled = element.is_enabled() # 元素是否启用 is_selected = element.is_selected() # 元素是否选中(复选框、单选按钮等) is_displayed = element.is_displayed() # 元素是否可见 # 特殊元素操作 # 下拉菜单 from selenium.webdriver.support.select import Select select = Select(driver.find_element(By.ID, “dropdown”)) select.select_by_visible_text(“Option 1”) # 通过文本选择 select.select_by_value(“option1”) # 通过值选择 select.select_by_index(1) # 通过索引选择 options = select.options # 获取所有选项 first_option = select.first_selected_option # 获取当前选中选项 select.deselect_all() # 取消所有选择(多选下拉框) # 复选框和单选按钮 checkbox = driver.find_element(By.ID, “checkbox”) ifnot checkbox.is_selected(): checkbox.click()
- 性能优化顺序:ID > Name > CSS > XPath 避免使用: 绝对XPath路径(容易失效) 基于视觉位置的选择器 多级嵌套CSS选择器 推荐使用: 有意义的ID和名称属性 数据测试属性(如data-testid) 短而明确的CSS选择器 建议添加: 页面加载和元素的等待机制 查找元素的超时和重试机制 详细的错误处理机制
- 在Web自动化中,页面加载和元素渲染需要时间,等待机制至关重要。
- # 设置隐式等待时间(全局设置) driver.implicitly_wait(10) # 等待最多10秒直到元素出现 隐式等待会在查找元素时自动等待一段时间直到元素出现,如果在指定时间内未找到元素,则抛出异常。
- from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待元素可见 element = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, “element_id”)) ) # 等待元素可点击 element = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, “button_id”)) ) # 等待页面标题包含特定文本 WebDriverWait(driver, 10).until( EC.title_contains(“Home Page”) ) # 等待元素消失 WebDriverWait(driver, 10).until( EC.invisibility_of_element_located((By.CLASS_NAME, “loading”)) ) # 等待警告框出现 WebDriverWait(driver, 10).until( EC.alert_is_present() ) # 等待元素的文本内容满足条件 WebDriverWait(driver, 10).until( EC.text_to_be_present_in_element((By.ID, “status”), “Success”) ) # 等待元素的属性值满足条件 WebDriverWait(driver, 10).until( EC.text_to_be_present_in_element_attribute((By.ID, “input”), “value”, “text”) )
- from selenium.webdriver.support.ui import WebDriverWait # 自定义等待条件 def element_has_class(element, class_name): return class_name in element.get_attribute(“class”).split() # 使用自定义等待条件 element = driver.find_element(By.ID, “myElement”) WebDriverWait(driver, 10).until(lambda driver: element_has_class(element, “active”))
- from selenium.webdriver.support.ui import WebDriverWait from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException # 创建FluentWait实例 wait = WebDriverWait( driver, timeout=30, poll_frequency=2, # 每2秒检查一次 ignored_exceptions=[NoSuchElementException, StaleElementReferenceException] ) # 使用FluentWait element = wait.until(EC.element_to_be_clickable((By.ID, “myElement”)))
- Selenium操作可能会触发各种异常,合理的异常处理可以提高脚本的健壮性。
- from selenium.common.exceptions import ( NoSuchElementException, # 元素未找到 TimeoutException, # 等待超时 ElementNotVisibleException, # 元素不可见 ElementNotInteractableException, # 元素不可交互 StaleElementReferenceException, # 元素已过时(DOM已更新) WebDriverException, # WebDriver通用异常 InvalidSelectorException, # 无效的选择器 UnexpectedAlertPresentException, # 意外的警告框 NoAlertPresentException, # 没有警告框 SessionNotCreatedException, # 会话创建失败 ElementClickInterceptedException # 元素点击被拦截 )
- try: element = driver.find_element(By.ID, “non_existent_element”) element.click() except NoSuchElementException: print(“元素未找到”) except ElementNotInteractableException: print(“元素不可交互”) except Exception as e: print(f”发生其他异常: {e}”)
- def retry_click(driver, by, value, max_attempts=3, wait_time=1): “”” 尝试多次点击元素 “”” from time import sleep for attempt in range(max_attempts): try: element = driver.find_element(by, value) element.click() returnTrue except (NoSuchElementException, ElementNotInteractableException, ElementClickInterceptedException, StaleElementReferenceException) as e: if attempt == max_attempts – 1: print(f”无法点击元素,错误: {e}”) returnFalse sleep(wait_time) returnFalse
- def get_fresh_element(driver, by, value): “”” 获取一个新鲜的元素引用,避免StaleElementReferenceException “”” try: return driver.find_element(by, value) except StaleElementReferenceException: # 重新查找元素 return driver.find_element(by, value)
- import functools from time import sleep def retry(max_attempts=3, wait_time=1): “”” 函数重试装饰器 “”” def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): for attempt in range(max_attempts): try: return func(*args, **kwargs) except (NoSuchElementException, ElementNotInteractableException, StaleElementReferenceException) as e: if attempt == max_attempts – 1: raise e sleep(wait_time) return wrapper return decorator # 使用装饰器 @retry(max_attempts=5, wait_time=2) def click_element(driver, by, value): driver.find_element(by, value).click()
- 避免使用time.sleep():不灵活且低效 优先使用显式等待:更精确,可控性更强 结合使用隐式等待和显式等待:隐式等待作为全局保护,显式等待针对特定场景 设置合理的超时时间:不要过长或过短 捕获并处理超时异常:提供适当的恢复机制或用户友好的错误信息 为不同网络环境调整等待策略:可配置的超时参数
- 页面对象模型是一种设计模式,将页面的元素和操作封装在类中,使测试代码更加清晰和可维护。
- class BasePage: “”” 所有页面的基类 “”” def __init__(self, driver): self.driver = driver def find_element(self, locator): return self.driver.find_element(*locator) def find_elements(self, locator): return self.driver.find_elements(*locator) def click(self, locator): self.find_element(locator).click() def input_text(self, locator, text): element = self.find_element(locator) element.clear() element.send_keys(text) def get_text(self, locator): return self.find_element(locator).text def is_element_present(self, locator): try: self.find_element(locator) returnTrue except NoSuchElementException: returnFalse def wait_for_element(self, locator, timeout=10): try: WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) returnTrue except TimeoutException: returnFalse class LoginPage(BasePage): “”” 登录页面对象 “”” # 页面元素定位 _username_field = (By.ID, “username”) _password_field = (By.ID, “password”) _login_button = (By.ID, “login_button”) _error_message = (By.CLASS_NAME, “error-message”) def __init__(self, driver): super().__init__(driver) self.driver.get(“https://example.com/login”) def enter_username(self, username): self.input_text(self._username_field, username) return self def enter_password(self, password): self.input_text(self._password_field, password) return self def click_login(self): self.click(self._login_button) # 根据登录结果返回不同的页面对象 if”dashboard”in self.driver.current_url: return DashboardPage(self.driver) return self def login(self, username, password): self.enter_username(username) self.enter_password(password) return self.click_login() def get_error_message(self): if self.is_element_present(self._error_message): return self.get_text(self._error_message) return”” class DashboardPage(BasePage): “”” 仪表盘页面对象 “”” _welcome_message = (By.ID, “welcome”) _logout_button = (By.ID, “logout”) def is_loaded(self): return self.wait_for_element(self._welcome_message) def get_welcome_message(self): return self.get_text(self._welcome_message) def logout(self): self.click(self._logout_button) return LoginPage(self.driver)
- def test_login_success(): driver = webdriver.Chrome() try: login_page = LoginPage(driver) dashboard_page = login_page.login(“valid_user”, “valid_password”) assert dashboard_page.is_loaded() assert”Welcome”in dashboard_page.get_welcome_message() finally: driver.quit() def test_login_failure(): driver = webdriver.Chrome() try: login_page = LoginPage(driver) result_page = login_page.login(“invalid_user”, “invalid_password”) assert isinstance(result_page, LoginPage) assert”Invalid credentials”in result_page.get_error_message() finally: driver.quit()
-
- import unittest from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager class TestLogin(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) self.driver.maximize_window() self.login_page = LoginPage(self.driver) def tearDown(self): self.driver.quit() def test_valid_login(self): dashboard_page = self.login_page.login(“valid_user”, “valid_password”) self.assertTrue(dashboard_page.is_loaded()) self.assertIn(“Welcome”, dashboard_page.get_welcome_message()) def test_invalid_login(self): result_page = self.login_page.login(“invalid_user”, “invalid_password”) self.assertIsInstance(result_page, LoginPage) self.assertIn(“Invalid credentials”, result_page.get_error_message()) if __name__ == “__main__”: unittest.main()
- import pytest from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager @pytest.fixture def driver(): # 设置 driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.maximize_window() yield driver # 清理 driver.quit() @pytest.fixture def login_page(driver): return LoginPage(driver) def test_valid_login(login_page): dashboard_page = login_page.login(“valid_user”, “valid_password”) assert dashboard_page.is_loaded() assert”Welcome”in dashboard_page.get_welcome_message() def test_invalid_login(login_page): result_page = login_page.login(“invalid_user”, “invalid_password”) assert isinstance(result_page, LoginPage) assert”Invalid credentials”in result_page.get_error_message()
- # features/login.feature Feature: User Login As a user I want to be able to login to the application So that I can access my account Scenario: Successful login with valid credentials Given the user is on the login page When the user enters “valid_user”as username And the user enters “valid_password”as password And the user clicks the login button Then the user should be redirected to the dashboard And the dashboard should display a welcome message Scenario: Failed login with invalid credentials Given the user is on the login page When the user enters “invalid_user”as username And the user enters “invalid_password”as password And the user clicks the login button Then the user should remain on the login page And an error message should be displayed # steps/login_steps.py from behave import given, when, then from pages.login_page import LoginPage from pages.dashboard_page import DashboardPage @given(‘the user is on the login page’) def step_impl(context): context.login_page = LoginPage(context.driver) @when(‘the user enters “{username}” as username’) def step_impl(context, username): context.login_page.enter_username(username) @when(‘the user enters “{password}” as password’) def step_impl(context, password): context.login_page.enter_password(password) @when(‘the user clicks the login button’) def step_impl(context): context.result_page = context.login_page.click_login() @then(‘the user should be redirected to the dashboard’) def step_impl(context): assert isinstance(context.result_page, DashboardPage) @then(‘the dashboard should display a welcome message’) def step_impl(context): assert”Welcome”in context.result_page.get_welcome_message() @then(‘the user should remain on the login page’) def step_impl(context): assert isinstance(context.result_page, LoginPage) @then(‘an error message should be displayed’) def step_impl(context): assert”Invalid credentials”in context.result_page.get_error_message()
-
- class PageFactory: “”” 页面对象工厂类 “”” @staticmethod def get_page(page_name, driver): pages = { “login”: LoginPage, “dashboard”: DashboardPage, “profile”: ProfilePage, “settings”: SettingsPage } if page_name.lower() notin pages: raise ValueError(f”不支持的页面: {page_name}”) return pages[page_name.lower()](driver)
- class WebDriverManager: “”” WebDriver管理器(单例模式) “”” _instance = None def __new__(cls): if cls._instance isNone: cls._instance = super(WebDriverManager, cls).__new__(cls) cls._instance.driver = None return cls._instance def get_driver(self, browser=”chrome”): if self.driver isNone: if browser.lower() == “chrome”: self.driver = webdriver.Chrome() elif browser.lower() == “firefox”: self.driver = webdriver.Firefox() else: raise ValueError(f”不支持的浏览器: {browser}”) self.driver.maximize_window() self.driver.implicitly_wait(10) return self.driver def quit(self): if self.driver: self.driver.quit() self.driver = None
- from abc import ABC, abstractmethod class WaitStrategy(ABC): “”” 等待策略基类 “”” @abstractmethod def wait_for(self, driver, locator): pass class VisibilityStrategy(WaitStrategy): “”” 等待元素可见策略 “”” def wait_for(self, driver, locator, timeout=10): return WebDriverWait(driver, timeout).until( EC.visibility_of_element_located(locator) ) class ClickableStrategy(WaitStrategy): “”” 等待元素可点击策略 “”” def wait_for(self, driver, locator, timeout=10): return WebDriverWait(driver, timeout).until( EC.element_to_be_clickable(locator) ) class PresenceStrategy(WaitStrategy): “”” 等待元素存在策略 “”” def wait_for(self, driver, locator, timeout=10): return WebDriverWait(driver, timeout).until( EC.presence_of_element_located(locator) ) # 使用策略模式的高级页面基类 class AdvancedBasePage: def __init__(self, driver): self.driver = driver self.wait_strategies = { “visible”: VisibilityStrategy(), “clickable”: ClickableStrategy(), “present”: PresenceStrategy() } def find_element(self, locator, strategy=”present”): return self.wait_strategies[strategy].wait_for(self.driver, locator)
-
- import json import os class ConfigManager: “”” 配置管理器 “”” _instance = None def __new__(cls): if cls._instance isNone: cls._instance = super(ConfigManager, cls).__new__(cls) cls._instance.config = {} cls._instance.load_config() return cls._instance def load_config(self, config_file=”config.json”): if os.path.exists(config_file): with open(config_file, “r”) as f: self.config = json.load(f) else: # 默认配置 self.config = { “browser”: “chrome”, “implicit_wait”: 10, “explicit_wait”: 20, “base_url”: “https://example.com”, “headless”: False, “screenshots_dir”: “screenshots”, “logs_dir”: “logs” } def get(self, key, default=None): return self.config.get(key, default)
- import logging import os from datetime import datetime class LogManager: “”” 日志管理器 “”” _instance = None def __new__(cls): if cls._instance isNone: cls._instance = super(LogManager, cls).__new__(cls) cls._instance.setup_logger() return cls._instance def setup_logger(self): config = ConfigManager().get(“logs”, {}) logs_dir = config.get(“dir”, “logs”) log_level = config.get(“level”, “INFO”) ifnot os.path.exists(logs_dir): os.makedirs(logs_dir) log_file = os.path.join(logs_dir, f”test_{datetime.now().strftime(‘%Y%m%d_%H%M%S’)}.log”) # 设置日志级别映射 level_map = { “DEBUG”: logging.DEBUG, “INFO”: logging.INFO, “WARNING”: logging.WARNING, “ERROR”: logging.ERROR, “CRITICAL”: logging.CRITICAL } # 设置根日志记录器 self.logger = logging.getLogger(“selenium_framework”) self.logger.setLevel(level_map.get(log_level.upper(), logging.INFO)) # 文件处理器 file_handler = logging.FileHandler(log_file) file_handler.setLevel(level_map.get(log_level.upper(), logging.INFO)) # 控制台处理器 console_handler = logging.StreamHandler() console_handler.setLevel(level_map.get(log_level.upper(), logging.INFO)) # 日志格式 formatter = logging.Formatter(‘%(asctime)s – %(name)s – %(levelname)s – %(message)s’) file_handler.setFormatter(formatter) console_handler.setFormatter(formatter) # 添加处理器 self.logger.addHandler(file_handler) self.logger.addHandler(console_handler) def get_logger(self): return self.logger
-
- from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys # 基本鼠标操作 def perform_hover(driver, element): “””执行悬停操作””” ActionChains(driver).move_to_element(element).perform() def perform_right_click(driver, element): “””执行右键点击操作””” ActionChains(driver).context_click(element).perform() def perform_double_click(driver, element): “””执行双击操作””” ActionChains(driver).double_click(element).perform() def perform_drag_and_drop(driver, source_element, target_element): “””执行拖放操作””” ActionChains(driver).drag_and_drop(source_element, target_element).perform() def perform_drag_and_drop_by_offset(driver, element, x_offset, y_offset): “””执行偏移拖放操作””” ActionChains(driver).drag_and_drop_by_offset(element, x_offset, y_offset).perform() # 组合键盘操作 def perform_ctrl_click(driver, element): “””执行Ctrl+点击操作(多选)””” ActionChains(driver).key_down(Keys.CONTROL).click(element).key_up(Keys.CONTROL).perform() def perform_shift_click(driver, element): “””执行Shift+点击操作(范围选择)””” ActionChains(driver).key_down(Keys.SHIFT).click(element).key_up(Keys.SHIFT).perform() def perform_select_all(driver): “””执行全选操作(Ctrl+A)””” ActionChains(driver).key_down(Keys.CONTROL).send_keys(‘a’).key_up(Keys.CONTROL).perform() def perform_copy(driver): “””执行复制操作(Ctrl+C)””” ActionChains(driver).key_down(Keys.CONTROL).send_keys(‘c’).key_up(Keys.CONTROL).perform() def perform_paste(driver): “””执行粘贴操作(Ctrl+V)””” ActionChains(driver).key_down(Keys.CONTROL).send_keys(‘v’).key_up(Keys.CONTROL).perform() # 链式组合操作 def perform_complex_action(driver, element1, element2): “””执行复杂组合操作””” ActionChains(driver) .move_to_element(element1) .pause(1) # 暂停1秒 .click() .move_to_element(element2) .click() .perform()
- def trigger_js_event(driver, element, event_name): “””触发JavaScript事件””” js_code = f”arguments[0].dispatchEvent(new Event(‘{event_name}’));” driver.execute_script(js_code, element) def focus_element(driver, element): “””使元素获取焦点””” driver.execute_script(“arguments[0].focus();”, element) def blur_element(driver, element): “””使元素失去焦点””” driver.execute_script(“arguments[0].blur();”, element) def scroll_to_element(driver, element): “””滚动到元素位置””” driver.execute_script(“arguments[0].scrollIntoView({behavior: ‘smooth’, block: ‘center’});”, element) def scroll_to_top(driver): “””滚动到页面顶部””” driver.execute_script(“window.scrollTo(0, 0);”) def scroll_to_bottom(driver): “””滚动到页面底部””” driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”)
- def switch_to_window_by_title(driver, title): “””切换到指定标题的窗口””” current_window = driver.current_window_handle for window in driver.window_handles: driver.switch_to.window(window) if title in driver.title: returnTrue # 如果没有找到匹配标题的窗口,切回原窗口 driver.switch_to.window(current_window) returnFalse def switch_to_window_by_url(driver, url_part): “””切换到URL包含指定部分的窗口””” current_window = driver.current_window_handle for window in driver.window_handles: driver.switch_to.window(window) if url_part in driver.current_url: returnTrue # 如果没有找到匹配URL的窗口,切回原窗口 driver.switch_to.window(current_window) returnFalse def close_all_windows_except_current(driver): “””关闭除当前窗口外的所有窗口””” current_window = driver.current_window_handle for window in driver.window_handles: if window != current_window: driver.switch_to.window(window) driver.close() driver.switch_to.window(current_window) def open_new_tab(driver, url=None): “””打开新标签页””” driver.execute_script(“window.open();”) driver.switch_to.window(driver.window_handles[-1]) if url: driver.get(url) def handle_popup_window(driver, action=”accept”): “””处理弹出窗口””” try: if action.lower() == “accept”: driver.switch_to.alert.accept() elif action.lower() == “dismiss”: driver.switch_to.alert.dismiss() elif action.lower() == “text”: return driver.switch_to.alert.text else: raise ValueError(f”不支持的操作: {action}”) returnTrue except: returnFalse
- def switch_to_frame_by_index(driver, index): “””通过索引切换到iframe””” try: driver.switch_to.frame(index) returnTrue except: returnFalse def switch_to_frame_by_name_or_id(driver, name_or_id): “””通过名称或ID切换到iframe””” try: driver.switch_to.frame(name_or_id) returnTrue except: returnFalse def switch_to_frame_by_element(driver, element): “””通过元素切换到iframe””” try: driver.switch_to.frame(element) returnTrue except: returnFalse def switch_to_parent_frame(driver): “””切换到父iframe””” try: driver.switch_to.parent_frame() returnTrue except: returnFalse def switch_to_default_content(driver): “””切换到主文档””” try: driver.switch_to.default_content() returnTrue except: returnFalse def get_iframe_count(driver): “””获取页面中iframe的数量””” return len(driver.find_elements(By.TAG_NAME, “iframe”)) def execute_in_iframe(driver, iframe_locator, action_func): “””在iframe中执行操作””” driver.switch_to.frame(driver.find_element(*iframe_locator)) try: result = action_func(driver) return result finally: driver.switch_to.default_content()
-
- def upload_file(driver, file_input_locator, file_path): “”” 上传文件(适用于<input type=”file”>元素) “”” try: file_input = driver.find_element(*file_input_locator) file_input.send_keys(file_path) returnTrue except Exception as e: print(f”文件上传失败: {e}”) returnFalse def upload_file_without_input(driver, upload_button_locator, file_path): “”” 上传文件(适用于没有可见<input type=”file”>的情况) 使用JS创建一个隐藏的文件输入元素 “”” try: # 创建一个隐藏的文件输入元素 js_script = “”” const input = document.createElement(‘input’); input.type = ‘file’; input.style.display = ‘none’; input.id = ‘hidden-file-input’; document.body.appendChild(input); return input; “”” file_input = driver.execute_script(js_script) # 设置文件路径 file_input.send_keys(file_path) # 触发上传按钮的点击事件 upload_button = driver.find_element(*upload_button_locator) driver.execute_script(“arguments[0].click();”, upload_button) # 移除隐藏的文件输入元素 driver.execute_script(“document.getElementById(‘hidden-file-input’).remove();”) returnTrue except Exception as e: print(f”文件上传失败: {e}”) returnFalse
- import os import time from pathlib import Path def setup_chrome_download_path(download_dir): “”” 设置Chrome浏览器的下载路径 “”” options = webdriver.ChromeOptions() prefs = { “download.default_directory”: download_dir, “download.prompt_for_download”: False, “download.directory_upgrade”: True, “safebrowsing.enabled”: True } options.add_experimental_option(“prefs”, prefs) return options def wait_for_download_to_complete(download_dir, timeout=60, check_interval=1): “”” 等待下载完成 “”” start_time = time.time() while time.time() – start_time < timeout: # 检查是否有部分下载的文件(.crdownload, .part等) downloading_files = list(Path(download_dir).glob(“*.crdownload”)) + list(Path(download_dir).glob(“*.part”)) ifnot downloading_files: # 找出最近下载的文件 downloaded_files = list(Path(download_dir).glob(“*”)) if downloaded_files: downloaded_files.sort(key=lambda x: x.stat().st_mtime, reverse=True) return str(downloaded_files[0]) time.sleep(check_interval) raise TimeoutError(“文件下载超时”) def download_file(driver, download_button_locator, download_dir, timeout=60): “”” 下载文件 “”” try: # 确保下载目录存在 os.makedirs(download_dir, exist_ok=True) # 点击下载按钮 download_button = driver.find_element(*download_button_locator) download_button.click() # 等待下载完成 downloaded_file = wait_for_download_to_complete(download_dir, timeout) return downloaded_file except Exception as e: print(f”文件下载失败: {e}”) returnNone
- import os import time from datetime import datetime def take_screenshot(driver, directory=”screenshots”, filename=None): “”” 截取屏幕截图 “”” try: # 确保目录存在 os.makedirs(directory, exist_ok=True) # 如果未指定文件名,使用时间戳生成 ifnot filename: timestamp = datetime.now().strftime(“%Y%m%d_%H%M%S”) filename = f”screenshot_{timestamp}.png” # 拼接完整路径 file_path = os.path.join(directory, filename) # 截图 driver.save_screenshot(file_path) return file_path except Exception as e: print(f”截图失败: {e}”) returnNone def take_element_screenshot(driver, element, directory=”screenshots”, filename=None): “”” 截取元素截图 “”” try: # 确保目录存在 os.makedirs(directory, exist_ok=True) # 如果未指定文件名,使用时间戳生成 ifnot filename: timestamp = datetime.now().strftime(“%Y%m%d_%H%M%S”) filename = f”element_screenshot_{timestamp}.png” # 拼接完整路径 file_path = os.path.join(directory, filename) # 截取元素截图 element.screenshot(file_path) return file_path except Exception as e: print(f”元素截图失败: {e}”) returnNone def screenshot_on_failure(func): “”” 失败时自动截图的装饰器 “”” def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as e: # 假设第一个参数是self,第二个参数是driver driver = args[1] if len(args) > 1elseNone if driver: timestamp = datetime.now().strftime(“%Y%m%d_%H%M%S”) filename = f”failure_{func.__name__}_{timestamp}.png” take_screenshot(driver, filename=filename) raise e return wrapper
- def verify_element_text(driver, locator, expected_text, contains=False): “”” 验证元素文本 “”” try: element = driver.find_element(*locator) actual_text = element.text if contains: assert expected_text in actual_text, f”期望文本包含'{expected_text}’,实际文本为'{actual_text}'” else: assert actual_text == expected_text, f”期望文本为'{expected_text}’,实际文本为'{actual_text}'” returnTrue except AssertionError as e: print(f”验证失败: {e}”) returnFalse def verify_element_attribute(driver, locator, attribute, expected_value, contains=False): “”” 验证元素属性 “”” try: element = driver.find_element(*locator) actual_value = element.get_attribute(attribute) if contains: assert expected_value in actual_value, f”期望属性'{attribute}’包含'{expected_value}’,实际值为'{actual_value}'” else: assert actual_value == expected_value, f”期望属性'{attribute}’为'{expected_value}’,实际值为'{actual_value}'” returnTrue except AssertionError as e: print(f”验证失败: {e}”) returnFalse def verify_element_visible(driver, locator, timeout=10): “”” 验证元素可见 “”” try: WebDriverWait(driver, timeout).until(EC.visibility_of_element_located(locator)) returnTrue except TimeoutException: print(f”元素在{timeout}秒内未可见: {locator}”) returnFalse def verify_element_not_visible(driver, locator, timeout=10): “”” 验证元素不可见 “”” try: WebDriverWait(driver, timeout).until(EC.invisibility_of_element_located(locator)) returnTrue except TimeoutException: print(f”元素在{timeout}秒内仍然可见: {locator}”) returnFalse def verify_url(driver, expected_url, contains=False, timeout=10): “”” 验证URL “”” try: if contains: WebDriverWait(driver, timeout).until(lambda d: expected_url in d.current_url) else: WebDriverWait(driver, timeout).until(lambda d: d.current_url == expected_url) returnTrue except TimeoutException: print(f”URL验证失败,期望URL{” if contains else ‘为’}{expected_url},实际URL为{driver.current_url}”) returnFalse def verify_title(driver, expected_title, contains=False, timeout=10): “”” 验证页面标题 “”” try: if contains: WebDriverWait(driver, timeout).until(lambda d: expected_title in d.title) else: WebDriverWait(driver, timeout).until(lambda d: d.title == expected_title) returnTrue except TimeoutException: print(f”标题验证失败,期望标题{” if contains else ‘为’}{expected_title},实际标题为{driver.title}”) returnFalse
-
- def measure_page_load_time(driver, url): “”” 测量页面加载时间 “”” start_time = time.time() driver.get(url) # 等待页面完全加载 WebDriverWait(driver, 60).until( lambda d: d.execute_script(“return document.readyState”) == “complete” ) end_time = time.time() load_time = end_time – start_time return load_time
- def get_performance_metrics(driver): “”” 获取浏览器性能指标 “”” # 使用Navigation Timing API navigation_timing = driver.execute_script(“”” var performance = window.performance; var timingObj = performance.timing; var loadTime = timingObj.loadEventEnd – timingObj.navigationStart; var dnsTime = timingObj.domainLookupEnd – timingObj.domainLookupStart; var tcpTime = timingObj.connectEnd – timingObj.connectStart; var serverTime = timingObj.responseEnd – timingObj.requestStart; var domTime = timingObj.domComplete – timingObj.domLoading; return { ‘loadTime’: loadTime, ‘dnsTime’: dnsTime, ‘tcpTime’: tcpTime, ‘serverTime’: serverTime, ‘domTime’: domTime, ‘firstPaint’: timingObj.responseStart – timingObj.navigationStart, ‘ttfb’: timingObj.responseStart – timingObj.requestStart }; “””) return navigation_timing def get_resource_timing(driver): “”” 获取资源加载时间 “”” resources = driver.execute_script(“”” var resources = window.performance.getEntriesByType(‘resource’); return resources.map(function(resource) { return { ‘name’: resource.name, ‘startTime’: resource.startTime, ‘duration’: resource.duration, ‘initiatorType’: resource.initiatorType, ‘size’: resource.transferSize }; }); “””) return resources
- def optimize_chrome_for_performance(): “”” 优化Chrome浏览器以提高性能 “”” options = webdriver.ChromeOptions() # 禁用不必要的浏览器功能 options.add_argument(“–disable-extensions”) options.add_argument(“–disable-gpu”) options.add_argument(“–disable-dev-shm-usage”) options.add_argument(“–disable-browser-side-navigation”) options.add_argument(“–disable-infobars”) options.add_argument(“–disable-notifications”) options.add_argument(“–disable-popup-blocking”) # 减少内存使用 options.add_argument(“–disable-features=site-per-process”) options.add_argument(“–process-per-site”) # 禁用图片加载以提高速度 prefs = { “profile.managed_default_content_settings.images”: 2, “profile.default_content_setting_values.notifications”: 2, “profile.default_content_setting_values.geolocation”: 2 } options.add_experimental_option(“prefs”, prefs) # 使用无头模式 options.add_argument(“–headless”) return options
- 获取浏览器控制台日志 def get_browser_logs(driver): “”” 获取浏览器控制台日志 “”” logs = driver.get_log(‘browser’) return logs def print_browser_logs(driver): “”” 打印浏览器控制台日志 “”” logs = driver.get_log(‘browser’) for log in logs: print(f”[{log[‘level’]}] {log[‘message’]}”) 以上就是从入门到精通介绍Python中Selenium安装使用的完整指南的详细内容,更多关于Python Selenium的资料请关注风君子博客其它相关文章! 您可能感兴趣的文章: Python Selenium打开指定路径浏览器的几种实现方法 python中selenium安装配置与使用示例 Python使用Selenium检查元素是否存在的方法 Python使用Selenium进行Web自动化的全过程 python selenium如何防止被浏览器检测 Python利用Selenium进行网页自动化与动态内容抓取操作 Python+Selenium实现无头浏览器网页截图
目录
- 环境准备与基础入门
- 1. 安装 Selenium 与浏览器驱动
- 安装 Selenium
- 安装浏览器驱动
- 2. Selenium 4.x 新特性
- 3. WebDriver初始化方法
- 使用Selenium Manager(推荐,Selenium 4.x)
- 传统方法(指定驱动路径)
- 配置浏览器选项
- 4. 基础浏览器操作
- 5. 常见浏览器配置
- 无头模式(Headless)
- 使用代理
- 禁用图片加载(提高性能)
- 元素定位与交互操作
- 1. 元素定位基础
- 2. 高级定位策略
- XPath进阶用法
- CSS选择器进阶用法
- 相对定位(Selenium 4.x新特性)
- 3. 查找多个元素
- 4. 元素交互操作
- 5. 元素查找最佳实践
- 等待机制与异常处理
- 1. 等待策略
- 隐式等待(Implicit Wait)
- 显式等待(Explicit Wait)
- 自定义等待条件
- 流畅等待(FluentWait)
- 2. 异常处理
- 常见异常类型
- 基本异常处理
- 重试机制
- 处理StaleElementReferenceException
- 使用装饰器处理异常
- 3. 等待策略最佳实践
- 面向对象封装与框架设计
- 1. 页面对象模型(Page Object Model, POM)
- 基本POM结构
- 使用POM进行测试
- 2. 测试框架集成
- 与unittest集成
- 与pytest集成
- 与Behave(BDD)集成
- 3. 高级框架设计模式
- 工厂模式
- 单例模式(驱动管理器)
- 策略模式(等待策略)
- 4. 配置与日志管理
- 配置管理
- 日志管理
- 进阶技巧与最佳实践
- 1. 高级交互操作
- ActionChains高级操作
- 处理JavaScript事件
- 2. 窗口与标签页管理
- 3. iframe处理
- 4. 文件上传与下载
- 文件上传
- 文件下载
- 5. 截图与日志
- 6. 高级断言与验证
- 性能优化与调试技巧
- 1. 性能测试与优化
- 测量页面加载时间
- 使用Performance API获取详细性能数据
- 优化执行速度
- 2. 高级调试技巧
# 使用pip安装最新版本
pip install selenium
# 安装特定版本
pip install selenium==4.10.0
# 在虚拟环境中安装(推荐)
python -m venv selenium_env
source selenium_env/bin/activate # Linux/Mac
selenium_envScriptsactivate.bat # Windows
pip install selenium
从Selenium 4.0开始,提供了自动管理驱动的功能,但了解手动安装方法仍然很重要:
Chrome浏览器:
- 访问 ChromeDriver 下载页面
- 下载与本地Chrome版本匹配的驱动程序
- 将驱动添加到系统PATH中或在代码中指定路径
Firefox浏览器:
- 访问 GeckoDriver 下载页面
- 下载适用于你操作系统的版本
- 将驱动添加到系统PATH中或在代码中指定路径
Edge浏览器:
- 访问 Microsoft Edge Driver 下载页面
- 下载与本地Edge版本匹配的驱动程序
Safari浏览器:
- Safari驱动已内置于macOS中
- 需要在Safari浏览器中启用开发者模式
Selenium 4.x引入了许多重要的改进和新功能:
- 相对定位:允许基于其他元素的位置来查找元素
- Service对象:用于更好地管理驱动程序服务
- WebDriver Manager:自动管理驱动程序的下载和设置
- CDP(Chrome DevTools Protocol)支持:允许访问浏览器特定的功能
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 自动管理驱动
driver = webdriver.Chrome()
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 指定驱动路径
service = Service(executable_path='/path/to/chromedriver')
driver = webdriver.Chrome(service=service)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# 创建Chrome选项对象
chrome_options = Options()
chrome_options.add_argument("--headless") # 无头模式
chrome_options.add_argument("--window-size=1920,1080") # 设置窗口大小
chrome_options.add_argument("--disable-gpu") # 禁用GPU加速
chrome_options.add_argument("--disable-extensions") # 禁用扩展
chrome_options.add_argument("--proxy-server='direct://'") # 代理设置
chrome_options.add_argument("--proxy-bypass-list=*") # 绕过代理
chrome_options.add_argument("--start-maximized") # 启动时最大化窗口
chrome_options.add_experimental_option("prefs", {
"download.default_directory": "/path/to/download/directory", # 设置下载目录
"download.prompt_for_download": False, # 禁用下载提示
"download.directory_upgrade": True,
"safebrowsing.enabled": True
})
# 初始化WebDriver
driver = webdriver.Chrome(options=chrome_options)
from selenium import webdriver
# 初始化WebDriver
driver = webdriver.Chrome()
# 窗口操作
driver.maximize_window() # 最大化窗口
driver.set_window_size(1920, 1080) # 设置窗口大小
driver.set_window_position(0, 0) # 设置窗口位置
# 导航操作
driver.get('https://www.example.com') # 打开URL
driver.back() # 后退
driver.forward() # 前进
driver.refresh() # 刷新页面
# 页面信息
title = driver.title # 获取页面标题
url = driver.current_url # 获取当前URL
page_source = driver.page_source # 获取页面源代码
# Cookie操作
driver.add_cookie({"name": "key", "value": "value"}) # 添加Cookie
cookies = driver.get_cookies() # 获取所有Cookies
driver.delete_cookie("key") # 删除特定Cookie
driver.delete_all_cookies() # 删除所有Cookies
# 关闭操作
driver.close() # 关闭当前标签页
driver.quit() # 关闭浏览器,释放资源
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument("--headless")
driver = webdriver.Chrome(options=chrome_options)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('--proxy-server=http://proxyserver:port')
driver = webdriver.Chrome(options=chrome_options)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
prefs = {"profile.managed_default_content_settings.images": 2}
chrome_options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=chrome_options)
Selenium提供了多种定位元素的方法,每种都有其适用场景:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# 1. 通过ID定位(最推荐,高效且唯一)
element = driver.find_element(By.ID, "login-button")
# 2. 通过Name属性定位
element = driver.find_element(By.NAME, "username")
# 3. 通过Class Name定位(不唯一时返回第一个匹配元素)
element = driver.find_element(By.CLASS_NAME, "login-form")
# 4. 通过Tag Name定位
element = driver.find_element(By.TAG_NAME, "button")
# 5. 通过Link Text定位(完全匹配)
element = driver.find_element(By.LINK_TEXT, "Forgot Password?")
# 6. 通过Partial Link Text定位(部分匹配)
element = driver.find_element(By.PARTIAL_LINK_TEXT, "Forgot")
# 7. 通过CSS选择器定位(强大且灵活)
element = driver.find_element(By.CSS_SELECTOR, "#login-form .submit-button")
# 8. 通过XPath定位(最强大但可能较慢)
element = driver.find_element(By.XPATH, "//div[@id='login-form']//button")
# 绝对路径(从根节点开始)
element = driver.find_element(By.XPATH, "/html/body/div/form/input")
# 相对路径(从任意节点开始)
element = driver.find_element(By.XPATH, "//input[@name='username']")
# 使用contains()函数
element = driver.find_element(By.XPATH, "//button[contains(@class, 'login')]")
# 使用text()函数
element = driver.find_element(By.XPATH, "//a[text()='Forgot Password?']")
element = driver.find_element(By.XPATH, "//a[contains(text(), 'Forgot')]")
# 使用AND和OR操作符
element = driver.find_element(By.XPATH, "//input[@type='text' and @name='username']")
element = driver.find_element(By.XPATH, "//button[@type='submit' or @type='button']")
# 通过父子关系定位
element = driver.find_element(By.XPATH, "//form[@id='login-form']/input")
parent = driver.find_element(By.XPATH, "//input[@id='username']/..")
# 通过兄弟关系定位
element = driver.find_element(By.XPATH, "//input[@id='username']/following-sibling::input")
element = driver.find_element(By.XPATH, "//input[@id='password']/preceding-sibling::input")
# 按索引定位
element = driver.find_element(By.XPATH, "(//input[@type='text'])[2]")
# 使用轴(axes)
element = driver.find_element(By.XPATH, "//input[@id='username']/ancestor::form")
element = driver.find_element(By.XPATH, "//form/descendant::input")
# 基本选择器
element = driver.find_element(By.CSS_SELECTOR, "#login-button") # ID选择器
element = driver.find_element(By.CSS_SELECTOR, ".login-form") # Class选择器
element = driver.find_element(By.CSS_SELECTOR, "input") # 标签选择器
# 属性选择器
element = driver.find_element(By.CSS_SELECTOR, "input[name='username']")
element = driver.find_element(By.CSS_SELECTOR, "input[name^='user']") # 以user开头
element = driver.find_element(By.CSS_SELECTOR, "input[name$='name']") # 以name结尾
element = driver.find_element(By.CSS_SELECTOR, "input[name*='erna']") # 包含erna
# 组合选择器
element = driver.find_element(By.CSS_SELECTOR, "form input[type='text']")
element = driver.find_element(By.CSS_SELECTOR, "form > input") # 直接子元素
element = driver.find_element(By.CSS_SELECTOR, "label + input") # 紧邻兄弟元素
element = driver.find_element(By.CSS_SELECTOR, "label ~ input") # 通用兄弟元素
# 伪类选择器
element = driver.find_element(By.CSS_SELECTOR, "input:first-child")
element = driver.find_element(By.CSS_SELECTOR, "input:last-child")
element = driver.find_element(By.CSS_SELECTOR, "input:nth-child(2)")
from selenium.webdriver.support.relative_locator import locate_with
# 获取参考元素
username_field = driver.find_element(By.ID, "username")
# 使用相对定位
password_field = driver.find_element(locate_with(By.TAG_NAME, "input").below(username_field))
login_button = driver.find_element(locate_with(By.TAG_NAME, "button").below(password_field))
remember_me = driver.find_element(locate_with(By.TAG_NAME, "input").to_right_of(password_field))
forgot_password = driver.find_element(locate_with(By.TAG_NAME, "a").above(login_button))
# 查找所有符合条件的元素
elements = driver.find_elements(By.CSS_SELECTOR, ".product-item")
# 遍历元素列表
for element in elements:
name = element.find_element(By.CLASS_NAME, "product-name").text
price = element.find_element(By.CLASS_NAME, "product-price").text
print(f"产品名称: {name}, 价格: {price}")
# 输入操作
element.send_keys("test@example.com") # 输入文本
element.send_keys(Keys.CONTROL, 'a') # 键盘组合键(全选)
element.send_keys(Keys.BACK_SPACE) # 退格键
# 点击操作
element.click() # 点击元素
element.submit() # 提交表单(适用于表单元素内)
# 清除操作
element.clear() # 清除文本输入框
# 获取元素属性和状态
value = element.get_attribute("value") # 获取属性值
text = element.text # 获取元素文本内容
tag = element.tag_name # 获取标签名
size = element.size # 获取元素大小
location = element.location # 获取元素位置
is_enabled = element.is_enabled() # 元素是否启用
is_selected = element.is_selected() # 元素是否选中(复选框、单选按钮等)
is_displayed = element.is_displayed() # 元素是否可见
# 特殊元素操作
# 下拉菜单
from selenium.webdriver.support.select import Select
select = Select(driver.find_element(By.ID, "dropdown"))
select.select_by_visible_text("Option 1") # 通过文本选择
select.select_by_value("option1") # 通过值选择
select.select_by_index(1) # 通过索引选择
options = select.options # 获取所有选项
first_option = select.first_selected_option # 获取当前选中选项
select.deselect_all() # 取消所有选择(多选下拉框)
# 复选框和单选按钮
checkbox = driver.find_element(By.ID, "checkbox")
ifnot checkbox.is_selected():
checkbox.click()
性能优化顺序:ID > Name > CSS > XPath
避免使用:
- 绝对XPath路径(容易失效)
- 基于视觉位置的选择器
- 多级嵌套CSS选择器
推荐使用:
- 有意义的ID和名称属性
- 数据测试属性(如
data-testid) - 短而明确的CSS选择器
建议添加:
- 页面加载和元素的等待机制
- 查找元素的超时和重试机制
- 详细的错误处理机制
在Web自动化中,页面加载和元素渲染需要时间,等待机制至关重要。
# 设置隐式等待时间(全局设置)
driver.implicitly_wait(10) # 等待最多10秒直到元素出现
隐式等待会在查找元素时自动等待一段时间直到元素出现,如果在指定时间内未找到元素,则抛出异常。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待元素可见
element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "element_id"))
)
# 等待元素可点击
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "button_id"))
)
# 等待页面标题包含特定文本
WebDriverWait(driver, 10).until(
EC.title_contains("Home Page")
)
# 等待元素消失
WebDriverWait(driver, 10).until(
EC.invisibility_of_element_located((By.CLASS_NAME, "loading"))
)
# 等待警告框出现
WebDriverWait(driver, 10).until(
EC.alert_is_present()
)
# 等待元素的文本内容满足条件
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.ID, "status"), "Success")
)
# 等待元素的属性值满足条件
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element_attribute((By.ID, "input"), "value", "text")
)
from selenium.webdriver.support.ui import WebDriverWait
# 自定义等待条件
def element_has_class(element, class_name):
return class_name in element.get_attribute("class").split()
# 使用自定义等待条件
element = driver.find_element(By.ID, "myElement")
WebDriverWait(driver, 10).until(lambda driver: element_has_class(element, "active"))
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException
# 创建FluentWait实例
wait = WebDriverWait(
driver,
timeout=30,
poll_frequency=2, # 每2秒检查一次
ignored_exceptions=[NoSuchElementException, StaleElementReferenceException]
)
# 使用FluentWait
element = wait.until(EC.element_to_be_clickable((By.ID, "myElement")))
Selenium操作可能会触发各种异常,合理的异常处理可以提高脚本的健壮性。
from selenium.common.exceptions import (
NoSuchElementException, # 元素未找到
TimeoutException, # 等待超时
ElementNotVisibleException, # 元素不可见
ElementNotInteractableException, # 元素不可交互
StaleElementReferenceException, # 元素已过时(DOM已更新)
WebDriverException, # WebDriver通用异常
InvalidSelectorException, # 无效的选择器
UnexpectedAlertPresentException, # 意外的警告框
NoAlertPresentException, # 没有警告框
SessionNotCreatedException, # 会话创建失败
ElementClickInterceptedException # 元素点击被拦截
)
try:
element = driver.find_element(By.ID, "non_existent_element")
element.click()
except NoSuchElementException:
print("元素未找到")
except ElementNotInteractableException:
print("元素不可交互")
except Exception as e:
print(f"发生其他异常: {e}")
def retry_click(driver, by, value, max_attempts=3, wait_time=1):
"""
尝试多次点击元素
"""
from time import sleep
for attempt in range(max_attempts):
try:
element = driver.find_element(by, value)
element.click()
returnTrue
except (NoSuchElementException, ElementNotInteractableException,
ElementClickInterceptedException, StaleElementReferenceException) as e:
if attempt == max_attempts - 1:
print(f"无法点击元素,错误: {e}")
returnFalse
sleep(wait_time)
returnFalse
def get_fresh_element(driver, by, value):
"""
获取一个新鲜的元素引用,避免StaleElementReferenceException
"""
try:
return driver.find_element(by, value)
except StaleElementReferenceException:
# 重新查找元素
return driver.find_element(by, value)
import functools
from time import sleep
def retry(max_attempts=3, wait_time=1):
"""
函数重试装饰器
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except (NoSuchElementException, ElementNotInteractableException,
StaleElementReferenceException) as e:
if attempt == max_attempts - 1:
raise e
sleep(wait_time)
return wrapper
return decorator
# 使用装饰器
@retry(max_attempts=5, wait_time=2)
def click_element(driver, by, value):
driver.find_element(by, value).click()
- 避免使用
time.sleep():不灵活且低效
- 优先使用显式等待:更精确,可控性更强
- 结合使用隐式等待和显式等待:隐式等待作为全局保护,显式等待针对特定场景
- 设置合理的超时时间:不要过长或过短
- 捕获并处理超时异常:提供适当的恢复机制或用户友好的错误信息
- 为不同网络环境调整等待策略:可配置的超时参数
time.sleep():不灵活且低效
页面对象模型是一种设计模式,将页面的元素和操作封装在类中,使测试代码更加清晰和可维护。
class BasePage:
"""
所有页面的基类
"""
def __init__(self, driver):
self.driver = driver
def find_element(self, locator):
return self.driver.find_element(*locator)
def find_elements(self, locator):
return self.driver.find_elements(*locator)
def click(self, locator):
self.find_element(locator).click()
def input_text(self, locator, text):
element = self.find_element(locator)
element.clear()
element.send_keys(text)
def get_text(self, locator):
return self.find_element(locator).text
def is_element_present(self, locator):
try:
self.find_element(locator)
returnTrue
except NoSuchElementException:
returnFalse
def wait_for_element(self, locator, timeout=10):
try:
WebDriverWait(self.driver, timeout).until(
EC.presence_of_element_located(locator)
)
returnTrue
except TimeoutException:
returnFalse
class LoginPage(BasePage):
"""
登录页面对象
"""
# 页面元素定位
_username_field = (By.ID, "username")
_password_field = (By.ID, "password")
_login_button = (By.ID, "login_button")
_error_message = (By.CLASS_NAME, "error-message")
def __init__(self, driver):
super().__init__(driver)
self.driver.get("https://example.com/login")
def enter_username(self, username):
self.input_text(self._username_field, username)
return self
def enter_password(self, password):
self.input_text(self._password_field, password)
return self
def click_login(self):
self.click(self._login_button)
# 根据登录结果返回不同的页面对象
if"dashboard"in self.driver.current_url:
return DashboardPage(self.driver)
return self
def login(self, username, password):
self.enter_username(username)
self.enter_password(password)
return self.click_login()
def get_error_message(self):
if self.is_element_present(self._error_message):
return self.get_text(self._error_message)
return""
class DashboardPage(BasePage):
"""
仪表盘页面对象
"""
_welcome_message = (By.ID, "welcome")
_logout_button = (By.ID, "logout")
def is_loaded(self):
return self.wait_for_element(self._welcome_message)
def get_welcome_message(self):
return self.get_text(self._welcome_message)
def logout(self):
self.click(self._logout_button)
return LoginPage(self.driver)
def test_login_success():
driver = webdriver.Chrome()
try:
login_page = LoginPage(driver)
dashboard_page = login_page.login("valid_user", "valid_password")
assert dashboard_page.is_loaded()
assert"Welcome"in dashboard_page.get_welcome_message()
finally:
driver.quit()
def test_login_failure():
driver = webdriver.Chrome()
try:
login_page = LoginPage(driver)
result_page = login_page.login("invalid_user", "invalid_password")
assert isinstance(result_page, LoginPage)
assert"Invalid credentials"in result_page.get_error_message()
finally:
driver.quit()
import unittest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
class TestLogin(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
self.driver.maximize_window()
self.login_page = LoginPage(self.driver)
def tearDown(self):
self.driver.quit()
def test_valid_login(self):
dashboard_page = self.login_page.login("valid_user", "valid_password")
self.assertTrue(dashboard_page.is_loaded())
self.assertIn("Welcome", dashboard_page.get_welcome_message())
def test_invalid_login(self):
result_page = self.login_page.login("invalid_user", "invalid_password")
self.assertIsInstance(result_page, LoginPage)
self.assertIn("Invalid credentials", result_page.get_error_message())
if __name__ == "__main__":
unittest.main()
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
@pytest.fixture
def driver():
# 设置
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.maximize_window()
yield driver
# 清理
driver.quit()
@pytest.fixture
def login_page(driver):
return LoginPage(driver)
def test_valid_login(login_page):
dashboard_page = login_page.login("valid_user", "valid_password")
assert dashboard_page.is_loaded()
assert"Welcome"in dashboard_page.get_welcome_message()
def test_invalid_login(login_page):
result_page = login_page.login("invalid_user", "invalid_password")
assert isinstance(result_page, LoginPage)
assert"Invalid credentials"in result_page.get_error_message()
# features/login.feature
Feature: User Login
As a user
I want to be able to login to the application
So that I can access my account
Scenario: Successful login with valid credentials
Given the user is on the login page
When the user enters "valid_user"as username
And the user enters "valid_password"as password
And the user clicks the login button
Then the user should be redirected to the dashboard
And the dashboard should display a welcome message
Scenario: Failed login with invalid credentials
Given the user is on the login page
When the user enters "invalid_user"as username
And the user enters "invalid_password"as password
And the user clicks the login button
Then the user should remain on the login page
And an error message should be displayed
# steps/login_steps.py
from behave import given, when, then
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage
@given('the user is on the login page')
def step_impl(context):
context.login_page = LoginPage(context.driver)
@when('the user enters "{username}" as username')
def step_impl(context, username):
context.login_page.enter_username(username)
@when('the user enters "{password}" as password')
def step_impl(context, password):
context.login_page.enter_password(password)
@when('the user clicks the login button')
def step_impl(context):
context.result_page = context.login_page.click_login()
@then('the user should be redirected to the dashboard')
def step_impl(context):
assert isinstance(context.result_page, DashboardPage)
@then('the dashboard should display a welcome message')
def step_impl(context):
assert"Welcome"in context.result_page.get_welcome_message()
@then('the user should remain on the login page')
def step_impl(context):
assert isinstance(context.result_page, LoginPage)
@then('an error message should be displayed')
def step_impl(context):
assert"Invalid credentials"in context.result_page.get_error_message()
class PageFactory:
"""
页面对象工厂类
"""
@staticmethod
def get_page(page_name, driver):
pages = {
"login": LoginPage,
"dashboard": DashboardPage,
"profile": ProfilePage,
"settings": SettingsPage
}
if page_name.lower() notin pages:
raise ValueError(f"不支持的页面: {page_name}")
return pages[page_name.lower()](driver)
class WebDriverManager:
"""
WebDriver管理器(单例模式)
"""
_instance = None
def __new__(cls):
if cls._instance isNone:
cls._instance = super(WebDriverManager, cls).__new__(cls)
cls._instance.driver = None
return cls._instance
def get_driver(self, browser="chrome"):
if self.driver isNone:
if browser.lower() == "chrome":
self.driver = webdriver.Chrome()
elif browser.lower() == "firefox":
self.driver = webdriver.Firefox()
else:
raise ValueError(f"不支持的浏览器: {browser}")
self.driver.maximize_window()
self.driver.implicitly_wait(10)
return self.driver
def quit(self):
if self.driver:
self.driver.quit()
self.driver = None
from abc import ABC, abstractmethod
class WaitStrategy(ABC):
"""
等待策略基类
"""
@abstractmethod
def wait_for(self, driver, locator):
pass
class VisibilityStrategy(WaitStrategy):
"""
等待元素可见策略
"""
def wait_for(self, driver, locator, timeout=10):
return WebDriverWait(driver, timeout).until(
EC.visibility_of_element_located(locator)
)
class ClickableStrategy(WaitStrategy):
"""
等待元素可点击策略
"""
def wait_for(self, driver, locator, timeout=10):
return WebDriverWait(driver, timeout).until(
EC.element_to_be_clickable(locator)
)
class PresenceStrategy(WaitStrategy):
"""
等待元素存在策略
"""
def wait_for(self, driver, locator, timeout=10):
return WebDriverWait(driver, timeout).until(
EC.presence_of_element_located(locator)
)
# 使用策略模式的高级页面基类
class AdvancedBasePage:
def __init__(self, driver):
self.driver = driver
self.wait_strategies = {
"visible": VisibilityStrategy(),
"clickable": ClickableStrategy(),
"present": PresenceStrategy()
}
def find_element(self, locator, strategy="present"):
return self.wait_strategies[strategy].wait_for(self.driver, locator)
import json
import os
class ConfigManager:
"""
配置管理器
"""
_instance = None
def __new__(cls):
if cls._instance isNone:
cls._instance = super(ConfigManager, cls).__new__(cls)
cls._instance.config = {}
cls._instance.load_config()
return cls._instance
def load_config(self, config_file="config.json"):
if os.path.exists(config_file):
with open(config_file, "r") as f:
self.config = json.load(f)
else:
# 默认配置
self.config = {
"browser": "chrome",
"implicit_wait": 10,
"explicit_wait": 20,
"base_url": "https://example.com",
"headless": False,
"screenshots_dir": "screenshots",
"logs_dir": "logs"
}
def get(self, key, default=None):
return self.config.get(key, default)
import logging
import os
from datetime import datetime
class LogManager:
"""
日志管理器
"""
_instance = None
def __new__(cls):
if cls._instance isNone:
cls._instance = super(LogManager, cls).__new__(cls)
cls._instance.setup_logger()
return cls._instance
def setup_logger(self):
config = ConfigManager().get("logs", {})
logs_dir = config.get("dir", "logs")
log_level = config.get("level", "INFO")
ifnot os.path.exists(logs_dir):
os.makedirs(logs_dir)
log_file = os.path.join(logs_dir, f"test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
# 设置日志级别映射
level_map = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL
}
# 设置根日志记录器
self.logger = logging.getLogger("selenium_framework")
self.logger.setLevel(level_map.get(log_level.upper(), logging.INFO))
# 文件处理器
file_handler = logging.FileHandler(log_file)
file_handler.setLevel(level_map.get(log_level.upper(), logging.INFO))
# 控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(level_map.get(log_level.upper(), logging.INFO))
# 日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 添加处理器
self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)
def get_logger(self):
return self.logger
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
# 基本鼠标操作
def perform_hover(driver, element):
"""执行悬停操作"""
ActionChains(driver).move_to_element(element).perform()
def perform_right_click(driver, element):
"""执行右键点击操作"""
ActionChains(driver).context_click(element).perform()
def perform_double_click(driver, element):
"""执行双击操作"""
ActionChains(driver).double_click(element).perform()
def perform_drag_and_drop(driver, source_element, target_element):
"""执行拖放操作"""
ActionChains(driver).drag_and_drop(source_element, target_element).perform()
def perform_drag_and_drop_by_offset(driver, element, x_offset, y_offset):
"""执行偏移拖放操作"""
ActionChains(driver).drag_and_drop_by_offset(element, x_offset, y_offset).perform()
# 组合键盘操作
def perform_ctrl_click(driver, element):
"""执行Ctrl+点击操作(多选)"""
ActionChains(driver).key_down(Keys.CONTROL).click(element).key_up(Keys.CONTROL).perform()
def perform_shift_click(driver, element):
"""执行Shift+点击操作(范围选择)"""
ActionChains(driver).key_down(Keys.SHIFT).click(element).key_up(Keys.SHIFT).perform()
def perform_select_all(driver):
"""执行全选操作(Ctrl+A)"""
ActionChains(driver).key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()
def perform_copy(driver):
"""执行复制操作(Ctrl+C)"""
ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
def perform_paste(driver):
"""执行粘贴操作(Ctrl+V)"""
ActionChains(driver).key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()
# 链式组合操作
def perform_complex_action(driver, element1, element2):
"""执行复杂组合操作"""
ActionChains(driver)
.move_to_element(element1)
.pause(1) # 暂停1秒
.click()
.move_to_element(element2)
.click()
.perform()
def trigger_js_event(driver, element, event_name):
"""触发JavaScript事件"""
js_code = f"arguments[0].dispatchEvent(new Event('{event_name}'));"
driver.execute_script(js_code, element)
def focus_element(driver, element):
"""使元素获取焦点"""
driver.execute_script("arguments[0].focus();", element)
def blur_element(driver, element):
"""使元素失去焦点"""
driver.execute_script("arguments[0].blur();", element)
def scroll_to_element(driver, element):
"""滚动到元素位置"""
driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", element)
def scroll_to_top(driver):
"""滚动到页面顶部"""
driver.execute_script("window.scrollTo(0, 0);")
def scroll_to_bottom(driver):
"""滚动到页面底部"""
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
def switch_to_window_by_title(driver, title):
"""切换到指定标题的窗口"""
current_window = driver.current_window_handle
for window in driver.window_handles:
driver.switch_to.window(window)
if title in driver.title:
returnTrue
# 如果没有找到匹配标题的窗口,切回原窗口
driver.switch_to.window(current_window)
returnFalse
def switch_to_window_by_url(driver, url_part):
"""切换到URL包含指定部分的窗口"""
current_window = driver.current_window_handle
for window in driver.window_handles:
driver.switch_to.window(window)
if url_part in driver.current_url:
returnTrue
# 如果没有找到匹配URL的窗口,切回原窗口
driver.switch_to.window(current_window)
returnFalse
def close_all_windows_except_current(driver):
"""关闭除当前窗口外的所有窗口"""
current_window = driver.current_window_handle
for window in driver.window_handles:
if window != current_window:
driver.switch_to.window(window)
driver.close()
driver.switch_to.window(current_window)
def open_new_tab(driver, url=None):
"""打开新标签页"""
driver.execute_script("window.open();")
driver.switch_to.window(driver.window_handles[-1])
if url:
driver.get(url)
def handle_popup_window(driver, action="accept"):
"""处理弹出窗口"""
try:
if action.lower() == "accept":
driver.switch_to.alert.accept()
elif action.lower() == "dismiss":
driver.switch_to.alert.dismiss()
elif action.lower() == "text":
return driver.switch_to.alert.text
else:
raise ValueError(f"不支持的操作: {action}")
returnTrue
except:
returnFalse
def switch_to_frame_by_index(driver, index):
"""通过索引切换到iframe"""
try:
driver.switch_to.frame(index)
returnTrue
except:
returnFalse
def switch_to_frame_by_name_or_id(driver, name_or_id):
"""通过名称或ID切换到iframe"""
try:
driver.switch_to.frame(name_or_id)
returnTrue
except:
returnFalse
def switch_to_frame_by_element(driver, element):
"""通过元素切换到iframe"""
try:
driver.switch_to.frame(element)
returnTrue
except:
returnFalse
def switch_to_parent_frame(driver):
"""切换到父iframe"""
try:
driver.switch_to.parent_frame()
returnTrue
except:
returnFalse
def switch_to_default_content(driver):
"""切换到主文档"""
try:
driver.switch_to.default_content()
returnTrue
except:
returnFalse
def get_iframe_count(driver):
"""获取页面中iframe的数量"""
return len(driver.find_elements(By.TAG_NAME, "iframe"))
def execute_in_iframe(driver, iframe_locator, action_func):
"""在iframe中执行操作"""
driver.switch_to.frame(driver.find_element(*iframe_locator))
try:
result = action_func(driver)
return result
finally:
driver.switch_to.default_content()
def upload_file(driver, file_input_locator, file_path):
"""
上传文件(适用于<input type="file">元素)
"""
try:
file_input = driver.find_element(*file_input_locator)
file_input.send_keys(file_path)
returnTrue
except Exception as e:
print(f"文件上传失败: {e}")
returnFalse
def upload_file_without_input(driver, upload_button_locator, file_path):
"""
上传文件(适用于没有可见<input type="file">的情况)
使用JS创建一个隐藏的文件输入元素
"""
try:
# 创建一个隐藏的文件输入元素
js_script = """
const input = document.createElement('input');
input.type = 'file';
input.style.display = 'none';
input.id = 'hidden-file-input';
document.body.appendChild(input);
return input;
"""
file_input = driver.execute_script(js_script)
# 设置文件路径
file_input.send_keys(file_path)
# 触发上传按钮的点击事件
upload_button = driver.find_element(*upload_button_locator)
driver.execute_script("arguments[0].click();", upload_button)
# 移除隐藏的文件输入元素
driver.execute_script("document.getElementById('hidden-file-input').remove();")
returnTrue
except Exception as e:
print(f"文件上传失败: {e}")
returnFalse
import os
import time
from pathlib import Path
def setup_chrome_download_path(download_dir):
"""
设置Chrome浏览器的下载路径
"""
options = webdriver.ChromeOptions()
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
options.add_experimental_option("prefs", prefs)
return options
def wait_for_download_to_complete(download_dir, timeout=60, check_interval=1):
"""
等待下载完成
"""
start_time = time.time()
while time.time() - start_time < timeout:
# 检查是否有部分下载的文件(.crdownload, .part等)
downloading_files = list(Path(download_dir).glob("*.crdownload")) + list(Path(download_dir).glob("*.part"))
ifnot downloading_files:
# 找出最近下载的文件
downloaded_files = list(Path(download_dir).glob("*"))
if downloaded_files:
downloaded_files.sort(key=lambda x: x.stat().st_mtime, reverse=True)
return str(downloaded_files[0])
time.sleep(check_interval)
raise TimeoutError("文件下载超时")
def download_file(driver, download_button_locator, download_dir, timeout=60):
"""
下载文件
"""
try:
# 确保下载目录存在
os.makedirs(download_dir, exist_ok=True)
# 点击下载按钮
download_button = driver.find_element(*download_button_locator)
download_button.click()
# 等待下载完成
downloaded_file = wait_for_download_to_complete(download_dir, timeout)
return downloaded_file
except Exception as e:
print(f"文件下载失败: {e}")
returnNone
import os
import time
from datetime import datetime
def take_screenshot(driver, directory="screenshots", filename=None):
"""
截取屏幕截图
"""
try:
# 确保目录存在
os.makedirs(directory, exist_ok=True)
# 如果未指定文件名,使用时间戳生成
ifnot filename:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"screenshot_{timestamp}.png"
# 拼接完整路径
file_path = os.path.join(directory, filename)
# 截图
driver.save_screenshot(file_path)
return file_path
except Exception as e:
print(f"截图失败: {e}")
returnNone
def take_element_screenshot(driver, element, directory="screenshots", filename=None):
"""
截取元素截图
"""
try:
# 确保目录存在
os.makedirs(directory, exist_ok=True)
# 如果未指定文件名,使用时间戳生成
ifnot filename:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"element_screenshot_{timestamp}.png"
# 拼接完整路径
file_path = os.path.join(directory, filename)
# 截取元素截图
element.screenshot(file_path)
return file_path
except Exception as e:
print(f"元素截图失败: {e}")
returnNone
def screenshot_on_failure(func):
"""
失败时自动截图的装饰器
"""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
# 假设第一个参数是self,第二个参数是driver
driver = args[1] if len(args) > 1elseNone
if driver:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"failure_{func.__name__}_{timestamp}.png"
take_screenshot(driver, filename=filename)
raise e
return wrapper
def verify_element_text(driver, locator, expected_text, contains=False):
"""
验证元素文本
"""
try:
element = driver.find_element(*locator)
actual_text = element.text
if contains:
assert expected_text in actual_text, f"期望文本包含'{expected_text}',实际文本为'{actual_text}'"
else:
assert actual_text == expected_text, f"期望文本为'{expected_text}',实际文本为'{actual_text}'"
returnTrue
except AssertionError as e:
print(f"验证失败: {e}")
returnFalse
def verify_element_attribute(driver, locator, attribute, expected_value, contains=False):
"""
验证元素属性
"""
try:
element = driver.find_element(*locator)
actual_value = element.get_attribute(attribute)
if contains:
assert expected_value in actual_value, f"期望属性'{attribute}'包含'{expected_value}',实际值为'{actual_value}'"
else:
assert actual_value == expected_value, f"期望属性'{attribute}'为'{expected_value}',实际值为'{actual_value}'"
returnTrue
except AssertionError as e:
print(f"验证失败: {e}")
returnFalse
def verify_element_visible(driver, locator, timeout=10):
"""
验证元素可见
"""
try:
WebDriverWait(driver, timeout).until(EC.visibility_of_element_located(locator))
returnTrue
except TimeoutException:
print(f"元素在{timeout}秒内未可见: {locator}")
returnFalse
def verify_element_not_visible(driver, locator, timeout=10):
"""
验证元素不可见
"""
try:
WebDriverWait(driver, timeout).until(EC.invisibility_of_element_located(locator))
returnTrue
except TimeoutException:
print(f"元素在{timeout}秒内仍然可见: {locator}")
returnFalse
def verify_url(driver, expected_url, contains=False, timeout=10):
"""
验证URL
"""
try:
if contains:
WebDriverWait(driver, timeout).until(lambda d: expected_url in d.current_url)
else:
WebDriverWait(driver, timeout).until(lambda d: d.current_url == expected_url)
returnTrue
except TimeoutException:
print(f"URL验证失败,期望URL{'' if contains else '为'}{expected_url},实际URL为{driver.current_url}")
returnFalse
def verify_title(driver, expected_title, contains=False, timeout=10):
"""
验证页面标题
"""
try:
if contains:
WebDriverWait(driver, timeout).until(lambda d: expected_title in d.title)
else:
WebDriverWait(driver, timeout).until(lambda d: d.title == expected_title)
returnTrue
except TimeoutException:
print(f"标题验证失败,期望标题{'' if contains else '为'}{expected_title},实际标题为{driver.title}")
returnFalse
def measure_page_load_time(driver, url):
"""
测量页面加载时间
"""
start_time = time.time()
driver.get(url)
# 等待页面完全加载
WebDriverWait(driver, 60).until(
lambda d: d.execute_script("return document.readyState") == "complete"
)
end_time = time.time()
load_time = end_time - start_time
return load_time
def get_performance_metrics(driver):
"""
获取浏览器性能指标
"""
# 使用Navigation Timing API
navigation_timing = driver.execute_script("""
var performance = window.performance;
var timingObj = performance.timing;
var loadTime = timingObj.loadEventEnd - timingObj.navigationStart;
var dnsTime = timingObj.domainLookupEnd - timingObj.domainLookupStart;
var tcpTime = timingObj.connectEnd - timingObj.connectStart;
var serverTime = timingObj.responseEnd - timingObj.requestStart;
var domTime = timingObj.domComplete - timingObj.domLoading;
return {
'loadTime': loadTime,
'dnsTime': dnsTime,
'tcpTime': tcpTime,
'serverTime': serverTime,
'domTime': domTime,
'firstPaint': timingObj.responseStart - timingObj.navigationStart,
'ttfb': timingObj.responseStart - timingObj.requestStart
};
""")
return navigation_timing
def get_resource_timing(driver):
"""
获取资源加载时间
"""
resources = driver.execute_script("""
var resources = window.performance.getEntriesByType('resource');
return resources.map(function(resource) {
return {
'name': resource.name,
'startTime': resource.startTime,
'duration': resource.duration,
'initiatorType': resource.initiatorType,
'size': resource.transferSize
};
});
""")
return resources
def optimize_chrome_for_performance():
"""
优化Chrome浏览器以提高性能
"""
options = webdriver.ChromeOptions()
# 禁用不必要的浏览器功能
options.add_argument("--disable-extensions")
options.add_argument("--disable-gpu")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-browser-side-navigation")
options.add_argument("--disable-infobars")
options.add_argument("--disable-notifications")
options.add_argument("--disable-popup-blocking")
# 减少内存使用
options.add_argument("--disable-features=site-per-process")
options.add_argument("--process-per-site")
# 禁用图片加载以提高速度
prefs = {
"profile.managed_default_content_settings.images": 2,
"profile.default_content_setting_values.notifications": 2,
"profile.default_content_setting_values.geolocation": 2
}
options.add_experimental_option("prefs", prefs)
# 使用无头模式
options.add_argument("--headless")
return options
获取浏览器控制台日志
def get_browser_logs(driver):
"""
获取浏览器控制台日志
"""
logs = driver.get_log('browser')
return logs
def print_browser_logs(driver):
"""
打印浏览器控制台日志
"""
logs = driver.get_log('browser')
for log in logs:
print(f"[{log['level']}] {log['message']}")
以上就是从入门到精通介绍Python中Selenium安装使用的完整指南的详细内容,更多关于Python Selenium的资料请关注风君子博客其它相关文章!
您可能感兴趣的文章:
- Python Selenium打开指定路径浏览器的几种实现方法
- python中selenium安装配置与使用示例
- Python使用Selenium检查元素是否存在的方法
- Python使用Selenium进行Web自动化的全过程
- python selenium如何防止被浏览器检测
- Python利用Selenium进行网页自动化与动态内容抓取操作
- Python+Selenium实现无头浏览器网页截图