简体中文 繁體中文 English Deutsch 한국 사람 بالعربية TÜRKÇE português คนไทย Français Japanese

站内搜索

搜索

活动公告

通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,将及时处理!
10-23 09:31

全面解析Selenium自动化网页操作从基础语法到高级应用实战指南

SunJu_FaceMall

3万

主题

153

科技点

3万

积分

大区版主

碾压王

积分
32103
发表于 2025-9-12 14:30:00 | 显示全部楼层 |阅读模式 [标记阅至此楼]

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

Selenium是一个强大的Web自动化测试工具,广泛应用于自动化测试、网页数据抓取和Web应用程序交互等领域。它支持多种浏览器和编程语言,使得开发者能够编写脚本来模拟用户在浏览器中的各种操作。本文将全面解析Selenium自动化网页操作,从基础语法到高级应用,帮助读者掌握这一重要工具。

1. Selenium基础

1.1 什么是Selenium

Selenium是一个用于Web应用程序测试的自动化工具集,它直接运行在浏览器中,就像真正的用户在操作一样。Selenium主要由以下几个部分组成:

• Selenium IDE:一个Firefox插件,用于录制和回放用户操作
• Selenium WebDriver:提供各种编程语言的API,用于控制浏览器
• Selenium Grid:用于在多台机器上并行运行测试

1.2 安装与配置

首先,我们需要安装Selenium库。以Python为例:
  1. pip install selenium
复制代码

Selenium需要通过浏览器驱动来控制浏览器。不同浏览器需要不同的驱动:

• Chrome: ChromeDriver
• Firefox: GeckoDriver
• Edge: EdgeDriver
• Safari: SafariDriver

以Chrome为例,下载ChromeDriver的步骤:

1. 查看Chrome版本:在Chrome地址栏输入chrome://settings/help
2. 下载对应版本的ChromeDriver:https://chromedriver.chromium.org/downloads
3. 将下载的驱动文件放在系统PATH路径下或指定路径
  1. from selenium import webdriver
  2. from selenium.webdriver.chrome.service import Service
  3. # 指定驱动路径
  4. service = Service(executable_path='path/to/chromedriver')
  5. driver = webdriver.Chrome(service=service)
  6. # 或者将驱动放在系统PATH路径下
  7. driver = webdriver.Chrome()
复制代码

2. 基本语法与元素定位

2.1 浏览器基本操作
  1. from selenium import webdriver
  2. from selenium.webdriver.common.by import By
  3. from selenium.webdriver.common.keys import Keys
  4. import time
  5. # 初始化浏览器
  6. driver = webdriver.Chrome()
  7. # 打开网页
  8. driver.get("https://www.example.com")
  9. # 获取页面标题
  10. print("页面标题:", driver.title)
  11. # 获取当前URL
  12. print("当前URL:", driver.current_url)
  13. # 浏览器前进后退
  14. driver.get("https://www.google.com")
  15. driver.back()  # 后退
  16. driver.forward()  # 前进
  17. # 刷新页面
  18. driver.refresh()
  19. # 设置浏览器窗口大小
  20. driver.set_window_size(1024, 768)
  21. # 最大化窗口
  22. driver.maximize_window()
  23. # 关闭浏览器
  24. driver.quit()  # 关闭所有窗口并退出驱动
  25. # driver.close()  # 关闭当前窗口
复制代码

2.2 元素定位方法

Selenium提供了多种元素定位方法,常用的有以下几种:
  1. element = driver.find_element(By.ID, "element_id")
复制代码
  1. element = driver.find_element(By.NAME, "element_name")
复制代码
  1. element = driver.find_element(By.CLASS_NAME, "element_class")
复制代码
  1. element = driver.find_element(By.TAG_NAME, "div")
复制代码
  1. element = driver.find_element(By.LINK_TEXT, "Click Here")
复制代码
  1. element = driver.find_element(By.PARTIAL_LINK_TEXT, "Click")
复制代码
  1. element = driver.find_element(By.CSS_SELECTOR, "div#main-content > ul > li:first-child")
复制代码
  1. element = driver.find_element(By.XPATH, "//div[@id='main-content']/ul/li[1]")
复制代码

2.3 多元素定位

当需要定位多个元素时,可以使用find_elements方法:
  1. elements = driver.find_elements(By.CLASS_NAME, "item")
  2. for element in elements:
  3.     print(element.text)
复制代码

2.4 元素定位策略

在实际应用中,选择合适的元素定位策略非常重要:

1. 优先使用ID:ID通常是唯一的,定位最稳定
2. 使用Name:当元素有name属性时,也是一个不错的选择
3. 使用CSS选择器:比XPath更快,语法更简洁
4. 使用XPath:当其他方法无法定位时,XPath提供了强大的定位能力
  1. # 实际示例:登录页面元素定位
  2. driver.get("https://example.com/login")
  3. # 用户名输入框 - 使用ID定位
  4. username_input = driver.find_element(By.ID, "username")
  5. username_input.send_keys("myusername")
  6. # 密码输入框 - 使用Name定位
  7. password_input = driver.find_element(By.NAME, "password")
  8. password_input.send_keys("mypassword")
  9. # 登录按钮 - 使用CSS选择器定位
  10. login_button = driver.find_element(By.CSS_SELECTOR, "button.btn-primary")
  11. login_button.click()
复制代码

3. 页面元素操作

3.1 基本操作
  1. # 清除输入框内容并输入新文本
  2. search_box = driver.find_element(By.ID, "search")
  3. search_box.clear()  # 清除现有内容
  4. search_box.send_keys("Selenium Tutorial")  # 输入文本
  5. # 模拟按键操作
  6. search_box.send_keys(Keys.RETURN)  # 按回车键
  7. search_box.send_keys(Keys.CONTROL, 'a')  # Ctrl+A 全选
复制代码
  1. # 普通点击
  2. submit_button = driver.find_element(By.ID, "submit")
  3. submit_button.click()
  4. # 使用JavaScript点击(有时元素不可见或被遮挡时使用)
  5. element = driver.find_element(By.ID, "hidden-button")
  6. driver.execute_script("arguments[0].click();", element)
复制代码
  1. # 获取元素文本
  2. element = driver.find_element(By.ID, "message")
  3. print("元素文本:", element.text)
  4. # 获取元素属性
  5. link = driver.find_element(By.ID, "home-link")
  6. print("链接地址:", link.get_attribute("href"))
  7. # 获取元素位置和大小
  8. element = driver.find_element(By.ID, "logo")
  9. print("元素位置:", element.location)
  10. print("元素大小:", element.size)
  11. # 检查元素是否可见
  12. print("元素是否可见:", element.is_displayed())
  13. # 检查元素是否可用
  14. print("元素是否可用:", element.is_enabled())
  15. # 检查元素是否被选中(单选框、复选框)
  16. checkbox = driver.find_element(By.ID, "remember-me")
  17. print("元素是否被选中:", checkbox.is_selected())
复制代码

3.2 高级交互
  1. from selenium.webdriver.support.ui import Select
  2. # 定位下拉框
  3. select_element = driver.find_element(By.ID, "country")
  4. select = Select(select_element)
  5. # 通过索引选择
  6. select.select_by_index(0)  # 选择第一个选项
  7. # 通过value属性选择
  8. select.select_by_value("us")  # 选择value="us"的选项
  9. # 通过可见文本选择
  10. select.select_by_visible_text("United States")  # 选择文本为"United States"的选项
  11. # 获取所有选项
  12. options = select.options
  13. for option in options:
  14.     print(option.text)
  15. # 获取已选选项
  16. selected_option = select.first_selected_option
  17. print("已选选项:", selected_option.text)
  18. # 检查是否多选
  19. print("是否为多选下拉框:", select.is_multiple)
  20. # 取消选择(仅适用于多选下拉框)
  21. select.deselect_by_index(0)
  22. select.deselect_by_value("us")
  23. select.deselect_by_visible_text("United States")
  24. select.deselect_all()  # 取消所有选择
复制代码
  1. from selenium.webdriver.common.action_chains import ActionChains
  2. # 创建ActionChains对象
  3. actions = ActionChains(driver)
  4. # 鼠标悬停
  5. element = driver.find_element(By.ID, "menu-item")
  6. actions.move_to_element(element).perform()
  7. # 右键点击
  8. context_menu = driver.find_element(By.ID, "right-click-target")
  9. actions.context_click(context_menu).perform()
  10. # 双击
  11. double_click_element = driver.find_element(By.ID, "double-click-target")
  12. actions.double_click(double_click_element).perform()
  13. # 拖拽
  14. source = driver.find_element(By.ID, "draggable")
  15. target = driver.find_element(By.ID, "droppable")
  16. actions.drag_and_drop(source, target).perform()
  17. # 点击并按住
  18. click_and_hold = driver.find_element(By.ID, "clickable")
  19. actions.click_and_hold(click_and_hold).perform()
  20. # 释放鼠标
  21. actions.release().perform()
复制代码
  1. from selenium.webdriver.common.keys import Keys
  2. # 输入文本
  3. input_field = driver.find_element(By.ID, "text-input")
  4. input_field.send_keys("Hello World")
  5. # 使用特殊键
  6. input_field.send_keys(Keys.CONTROL, 'a')  # 全选
  7. input_field.send_keys(Keys.CONTROL, 'c')  # 复制
  8. input_field.send_keys(Keys.CONTROL, 'v')  # 粘贴
  9. input_field.send_keys(Keys.BACKSPACE)    # 退格
  10. input_field.send_keys(Keys.DELETE)       # 删除
  11. input_field.send_keys(Keys.ENTER)        # 回车
  12. input_field.send_keys(Keys.TAB)           # Tab键
  13. input_field.send_keys(Keys.ESCAPE)       # Esc键
  14. input_field.send_keys(Keys.ARROW_UP)     # 上箭头
  15. input_field.send_keys(Keys.ARROW_DOWN)   # 下箭头
复制代码
  1. # 滚动到页面底部
  2. driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
  3. # 滚动到页面顶部
  4. driver.execute_script("window.scrollTo(0, 0);")
  5. # 滚动到特定元素
  6. element = driver.find_element(By.ID, "target-element")
  7. driver.execute_script("arguments[0].scrollIntoView();", element)
  8. # 水平滚动
  9. driver.execute_script("window.scrollBy(200, 0);")
  10. # 垂直滚动
  11. driver.execute_script("window.scrollBy(0, 200);")
复制代码

4. 等待机制

在Web自动化测试中,由于网络延迟、页面加载等原因,元素可能不会立即出现。为了解决这个问题,Selenium提供了几种等待机制。

4.1 强制等待
  1. import time
  2. # 强制等待3秒
  3. time.sleep(3)  # 不推荐使用,不够灵活
复制代码

4.2 隐式等待

隐式等待是全局性的,设置一次后,在整个WebDriver实例的生命周期内都有效。当查找元素时,如果元素没有立即出现,WebDriver会等待指定的时间,直到元素出现或超时。
  1. # 设置隐式等待时间为10秒
  2. driver.implicitly_wait(10)
  3. # 如果元素立即出现,不会等待
  4. # 如果元素没有立即出现,最多等待10秒
  5. element = driver.find_element(By.ID, "some-element")
复制代码

4.3 显式等待

显式等待是针对特定元素的等待,更加灵活。它允许您设置特定的条件,等待该条件成立后再继续执行。
  1. from selenium.webdriver.support.ui import WebDriverWait
  2. from selenium.webdriver.support import expected_conditions as EC
  3. # 创建WebDriverWait对象,最多等待10秒
  4. wait = WebDriverWait(driver, 10)
  5. # 等待元素可见
  6. element = wait.until(EC.visibility_of_element_located((By.ID, "some-element")))
  7. # 等待元素可点击
  8. element = wait.until(EC.element_to_be_clickable((By.ID, "submit-button")))
  9. element.click()
  10. # 等待元素出现在DOM中(不一定可见)
  11. element = wait.until(EC.presence_of_element_located((By.ID, "hidden-element")))
  12. # 等待文本出现在元素中
  13. wait.until(EC.text_to_be_present_in_element((By.ID, "status"), "Completed"))
  14. # 等待标题包含特定文本
  15. wait.until(EC.title_contains("Dashboard"))
  16. # 等待URL包含特定字符串
  17. wait.until(EC.url_contains("dashboard"))
  18. # 等待URL变为特定值
  19. wait.until(EC.url_to_be("https://example.com/dashboard"))
  20. # 等待元素不可见
  21. wait.until(EC.invisibility_of_element_located((By.ID, "loading-spinner")))
  22. # 自定义等待条件
  23. def element_has_css_class(driver, selector, css_class):
  24.     element = driver.find_element(By.CSS_SELECTOR, selector)
  25.     return css_class in element.get_attribute("class")
  26. wait.until(lambda driver: element_has_css_class(driver, "#my-element", "active"))
复制代码

4.4 Fluent Wait

Fluent Wait是显式等待的一种更灵活的实现,允许您设置轮询间隔和忽略的异常类型。
  1. from selenium.common.exceptions import NoSuchElementException
  2. # 创建FluentWait实例
  3. wait = WebDriverWait(driver, 10, poll_frequency=1, ignored_exceptions=[NoSuchElementException])
  4. # 使用FluentWait等待元素
  5. element = wait.until(lambda d: d.find_element(By.ID, "dynamic-element"))
复制代码

5. 高级应用

5.1 处理弹窗
  1. # 触发alert弹窗
  2. driver.find_element(By.ID, "show-alert").click()
  3. # 切换到alert
  4. alert = driver.switch_to.alert
  5. # 获取alert文本
  6. print("Alert文本:", alert.text)
  7. # 接受alert(点击确定)
  8. alert.accept()
  9. # 或者拒绝alert(点击取消)
  10. # alert.dismiss()
复制代码
  1. # 触发confirm弹窗
  2. driver.find_element(By.ID, "show-confirm").click()
  3. # 切换到confirm
  4. confirm = driver.switch_to.confirm
  5. # 获取confirm文本
  6. print("Confirm文本:", confirm.text)
  7. # 接受confirm(点击确定)
  8. confirm.accept()
  9. # 或者拒绝confirm(点击取消)
  10. # confirm.dismiss()
复制代码
  1. # 触发prompt弹窗
  2. driver.find_element(By.ID, "show-prompt").click()
  3. # 切换到prompt
  4. prompt = driver.switch_to.alert
  5. # 获取prompt文本
  6. print("Prompt文本:", prompt.text)
  7. # 在prompt中输入文本
  8. prompt.send_keys("Selenium自动化测试")
  9. # 接受prompt(点击确定)
  10. prompt.accept()
  11. # 或者拒绝prompt(点击取消)
  12. # prompt.dismiss()
复制代码

5.2 处理多窗口和标签页
  1. # 获取当前窗口句柄
  2. current_window = driver.current_window_handle
  3. # 获取所有窗口句柄
  4. all_windows = driver.window_handles
  5. # 点击链接打开新窗口
  6. driver.find_element(By.LINK_TEXT, "Open New Window").click()
  7. # 等待新窗口打开
  8. wait = WebDriverWait(driver, 10)
  9. wait.until(lambda d: len(d.window_handles) > 1)
  10. # 切换到新窗口
  11. for window in driver.window_handles:
  12.     if window != current_window:
  13.         driver.switch_to.window(window)
  14.         break
  15. # 在新窗口中操作
  16. print("新窗口标题:", driver.title)
  17. driver.find_element(By.ID, "search-box").send_keys("Selenium")
  18. # 关闭新窗口
  19. driver.close()
  20. # 切换回原窗口
  21. driver.switch_to.window(current_window)
复制代码

5.3 处理iframe
  1. # 切换到iframe(通过索引)
  2. driver.switch_to.frame(0)  # 第一个iframe
  3. # 切换到iframe(通过name或id)
  4. driver.switch_to.frame("frame_name")
  5. # 切换到iframe(通过元素)
  6. iframe_element = driver.find_element(By.ID, "iframe_id")
  7. driver.switch_to.frame(iframe_element)
  8. # 在iframe中操作
  9. driver.find_element(By.ID, "inside-iframe").click()
  10. # 切换回主文档
  11. driver.switch_to.default_content()
  12. # 处理嵌套iframe
  13. driver.switch_to.frame("parent_frame")
  14. driver.switch_to.frame("child_frame")
  15. # 操作...
  16. driver.switch_to.parent_frame()  # 切换回父iframe
  17. driver.switch_to.default_content()  # 切换回主文档
复制代码

5.4 处理Cookie
  1. # 获取所有Cookie
  2. cookies = driver.get_cookies()
  3. for cookie in cookies:
  4.     print(cookie)
  5. # 获取特定Cookie
  6. cookie = driver.get_cookie("session_id")
  7. print(cookie)
  8. # 添加Cookie
  9. driver.add_cookie({"name": "username", "value": "john_doe"})
  10. # 删除特定Cookie
  11. driver.delete_cookie("username")
  12. # 删除所有Cookie
  13. driver.delete_all_cookies()
复制代码

5.5 执行JavaScript
  1. # 执行JavaScript并获取返回值
  2. title = driver.execute_script("return document.title;")
  3. print("页面标题:", title)
  4. # 执行JavaScript修改元素
  5. driver.execute_script("document.getElementById('my-element').style.display = 'none';")
  6. # 传递参数给JavaScript
  7. element = driver.find_element(By.ID, "my-element")
  8. driver.execute_script("arguments[0].style.border = '3px solid red';", element)
  9. # 获取页面滚动位置
  10. scroll_position = driver.execute_script("return window.pageYOffset;")
  11. print("垂直滚动位置:", scroll_position)
  12. # 滚动到元素位置
  13. element = driver.find_element(By.ID, "target-element")
  14. driver.execute_script("arguments[0].scrollIntoView(true);", element)
复制代码

5.6 截图功能
  1. # 截取整个窗口截图
  2. driver.save_screenshot("screenshot.png")
  3. # 截取特定元素截图
  4. element = driver.find_element(By.ID, "header")
  5. element.screenshot("header.png")
  6. # 使用PIL库进行截图处理
  7. from PIL import Image
  8. # 截图并裁剪
  9. driver.save_screenshot("full_page.png")
  10. full_image = Image.open("full_page.png")
  11. element = driver.find_element(By.ID, "content")
  12. location = element.location
  13. size = element.size
  14. left = location['x']
  15. top = location['y']
  16. right = left + size['width']
  17. bottom = top + size['height']
  18. element_image = full_image.crop((left, top, right, bottom))
  19. element_image.save("element_screenshot.png")
复制代码

6. 框架设计与最佳实践

6.1 Page Object模式

Page Object模式是一种设计模式,用于提高测试代码的可维护性和可读性。它将每个页面封装为一个类,页面的元素和操作作为类的方法。
  1. # pages/login_page.py
  2. from selenium.webdriver.common.by import By
  3. from selenium.webdriver.support.ui import WebDriverWait
  4. from selenium.webdriver.support import expected_conditions as EC
  5. class LoginPage:
  6.     def __init__(self, driver):
  7.         self.driver = driver
  8.         self.url = "https://example.com/login"
  9.         
  10.         # 元素定位器
  11.         self.username_input_locator = (By.ID, "username")
  12.         self.password_input_locator = (By.ID, "password")
  13.         self.login_button_locator = (By.ID, "login-button")
  14.         self.error_message_locator = (By.ID, "error-message")
  15.    
  16.     def load(self):
  17.         self.driver.get(self.url)
  18.         return self
  19.    
  20.     def login(self, username, password):
  21.         self.driver.find_element(*self.username_input_locator).send_keys(username)
  22.         self.driver.find_element(*self.password_input_locator).send_keys(password)
  23.         self.driver.find_element(*self.login_button_locator).click()
  24.    
  25.     def get_error_message(self):
  26.         wait = WebDriverWait(self.driver, 10)
  27.         error_element = wait.until(EC.visibility_of_element_located(self.error_message_locator))
  28.         return error_element.text
  29. # tests/test_login.py
  30. import unittest
  31. from selenium import webdriver
  32. from pages.login_page import LoginPage
  33. class TestLogin(unittest.TestCase):
  34.     def setUp(self):
  35.         self.driver = webdriver.Chrome()
  36.    
  37.     def tearDown(self):
  38.         self.driver.quit()
  39.    
  40.     def test_successful_login(self):
  41.         login_page = LoginPage(self.driver)
  42.         login_page.load()
  43.         login_page.login("valid_user", "valid_password")
  44.         # 断言登录成功后的页面元素或URL
  45.    
  46.     def test_invalid_login(self):
  47.         login_page = LoginPage(self.driver)
  48.         login_page.load()
  49.         login_page.login("invalid_user", "invalid_password")
  50.         error_message = login_page.get_error_message()
  51.         self.assertEqual(error_message, "Invalid username or password")
  52. if __name__ == "__main__":
  53.     unittest.main()
复制代码

6.2 数据驱动测试

数据驱动测试是一种测试方法,将测试数据与测试逻辑分离,使同一测试逻辑可以使用不同的测试数据运行多次。
  1. import unittest
  2. import csv
  3. from selenium import webdriver
  4. from pages.login_page import LoginPage
  5. class TestLoginDataDriven(unittest.TestCase):
  6.     def setUp(self):
  7.         self.driver = webdriver.Chrome()
  8.         self.login_page = LoginPage(self.driver)
  9.    
  10.     def tearDown(self):
  11.         self.driver.quit()
  12.    
  13.     def test_login_with_data(self):
  14.         # 从CSV文件读取测试数据
  15.         with open('test_data/login_data.csv', 'r') as file:
  16.             reader = csv.DictReader(file)
  17.             for row in reader:
  18.                 with self.subTest(username=row['username'], password=row['password']):
  19.                     self.login_page.load()
  20.                     self.login_page.login(row['username'], row['password'])
  21.                     
  22.                     if row['expected_result'] == 'success':
  23.                         # 断言登录成功
  24.                         self.assertIn('dashboard', self.driver.current_url)
  25.                     else:
  26.                         # 断言登录失败
  27.                         error_message = self.login_page.get_error_message()
  28.                         self.assertEqual(error_message, "Invalid username or password")
  29. if __name__ == "__main__":
  30.     unittest.main()
复制代码

6.3 测试框架集成
  1. # conftest.py
  2. import pytest
  3. from selenium import webdriver
  4. @pytest.fixture
  5. def browser():
  6.     driver = webdriver.Chrome()
  7.     driver.implicitly_wait(10)
  8.     yield driver
  9.     driver.quit()
  10. # test_login_pytest.py
  11. from pages.login_page import LoginPage
  12. def test_successful_login(browser):
  13.     login_page = LoginPage(browser)
  14.     login_page.load()
  15.     login_page.login("valid_user", "valid_password")
  16.     assert "dashboard" in browser.current_url
  17. def test_invalid_login(browser):
  18.     login_page = LoginPage(browser)
  19.     login_page.load()
  20.     login_page.login("invalid_user", "invalid_password")
  21.     error_message = login_page.get_error_message()
  22.     assert error_message == "Invalid username or password"
复制代码
  1. # features/login.feature
  2. Feature: User login
  3.   Scenario: Successful login
  4.     Given I am on the login page
  5.     When I enter valid username and password
  6.     And I click the login button
  7.     Then I should be redirected to the dashboard
  8.   Scenario: Invalid login
  9.     Given I am on the login page
  10.     When I enter invalid username and password
  11.     And I click the login button
  12.     Then I should see an error message
  13. # features/steps/login_steps.py
  14. from behave import given, when, then
  15. from pages.login_page import LoginPage
  16. @given('I am on the login page')
  17. def step_impl(context):
  18.     context.login_page = LoginPage(context.browser)
  19.     context.login_page.load()
  20. @when('I enter valid username and password')
  21. def step_impl(context):
  22.     context.login_page.login("valid_user", "valid_password")
  23. @when('I enter invalid username and password')
  24. def step_impl(context):
  25.     context.login_page.login("invalid_user", "invalid_password")
  26. @when('I click the login button')
  27. def step_impl(context):
  28.     # 登录操作已经在login方法中包含了点击按钮
  29.     pass
  30. @then('I should be redirected to the dashboard')
  31. def step_impl(context):
  32.     assert "dashboard" in context.browser.current_url
  33. @then('I should see an error message')
  34. def step_impl(context):
  35.     error_message = context.login_page.get_error_message()
  36.     assert error_message == "Invalid username or password"
  37. # environment.py
  38. from selenium import webdriver
  39. def before_all(context):
  40.     context.browser = webdriver.Chrome()
  41.     context.browser.implicitly_wait(10)
  42. def after_all(context):
  43.     context.browser.quit()
复制代码

6.4 配置管理

使用配置文件管理测试环境、浏览器类型、等待时间等参数:
  1. # config/config.py
  2. import os
  3. from configparser import ConfigParser
  4. class Config:
  5.     def __init__(self, config_file='config/config.ini'):
  6.         self.config = ConfigParser()
  7.         self.config.read(config_file)
  8.    
  9.     @property
  10.     def base_url(self):
  11.         return self.config.get('default', 'base_url')
  12.    
  13.     @property
  14.     def browser_type(self):
  15.         return self.config.get('default', 'browser_type')
  16.    
  17.     @property
  18.     def implicit_wait(self):
  19.         return self.config.getint('default', 'implicit_wait')
  20.    
  21.     @property
  22.     def explicit_wait(self):
  23.         return self.config.getint('default', 'explicit_wait')
  24.    
  25.     @property
  26.     def username(self):
  27.         return self.config.get('credentials', 'username')
  28.    
  29.     @property
  30.     def password(self):
  31.         return self.config.get('credentials', 'password')
  32. # config/config.ini
  33. [default]
  34. base_url = https://example.com
  35. browser_type = chrome
  36. implicit_wait = 10
  37. explicit_wait = 20
  38. [credentials]
  39. username = test_user
  40. password = test_password
复制代码

6.5 日志管理
  1. # utils/logger.py
  2. import logging
  3. import os
  4. from datetime import datetime
  5. class Logger:
  6.     def __init__(self, logger_name="automation"):
  7.         # 创建logger
  8.         self.logger = logging.getLogger(logger_name)
  9.         self.logger.setLevel(logging.DEBUG)
  10.         
  11.         # 创建日志目录
  12.         log_dir = "logs"
  13.         if not os.path.exists(log_dir):
  14.             os.makedirs(log_dir)
  15.         
  16.         # 创建文件处理器
  17.         log_file = os.path.join(log_dir, f"automation_{datetime.now().strftime('%Y%m%d')}.log")
  18.         file_handler = logging.FileHandler(log_file)
  19.         file_handler.setLevel(logging.DEBUG)
  20.         
  21.         # 创建控制台处理器
  22.         console_handler = logging.StreamHandler()
  23.         console_handler.setLevel(logging.INFO)
  24.         
  25.         # 创建格式化器
  26.         formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  27.         file_handler.setFormatter(formatter)
  28.         console_handler.setFormatter(formatter)
  29.         
  30.         # 添加处理器到logger
  31.         self.logger.addHandler(file_handler)
  32.         self.logger.addHandler(console_handler)
  33.    
  34.     def debug(self, message):
  35.         self.logger.debug(message)
  36.    
  37.     def info(self, message):
  38.         self.logger.info(message)
  39.    
  40.     def warning(self, message):
  41.         self.logger.warning(message)
  42.    
  43.     def error(self, message):
  44.         self.logger.error(message)
  45.    
  46.     def critical(self, message):
  47.         self.logger.critical(message)
复制代码

7. 实战案例:自动化测试电商网站

7.1 项目结构
  1. ecommerce_automation/
  2. ├── config/
  3. │   ├── config.ini
  4. │   └── __init__.py
  5. ├── pages/
  6. │   ├── __init__.py
  7. │   ├── base_page.py
  8. │   ├── home_page.py
  9. │   ├── login_page.py
  10. │   ├── product_page.py
  11. │   ├── cart_page.py
  12. │   └── checkout_page.py
  13. ├── tests/
  14. │   ├── __init__.py
  15. │   ├── conftest.py
  16. │   ├── test_login.py
  17. │   ├── test_search.py
  18. │   ├── test_add_to_cart.py
  19. │   └── test_checkout.py
  20. ├── utils/
  21. │   ├── __init__.py
  22. │   ├── logger.py
  23. │   ├── excel_utils.py
  24. │   └── screenshot_utils.py
  25. ├── data/
  26. │   ├── login_data.csv
  27. │   └── product_data.csv
  28. ├── reports/
  29. ├── logs/
  30. ├── requirements.txt
  31. └── run_tests.py
复制代码

7.2 基础页面类
  1. # pages/base_page.py
  2. from selenium.webdriver.support.ui import WebDriverWait
  3. from selenium.webdriver.support import expected_conditions as EC
  4. from utils.logger import Logger
  5. class BasePage:
  6.     def __init__(self, driver):
  7.         self.driver = driver
  8.         self.wait = WebDriverWait(driver, 10)
  9.         self.logger = Logger()
  10.    
  11.     def find_element(self, *locator):
  12.         return self.driver.find_element(*locator)
  13.    
  14.     def find_elements(self, *locator):
  15.         return self.driver.find_elements(*locator)
  16.    
  17.     def click(self, locator):
  18.         element = self.wait.until(EC.element_to_be_clickable(locator))
  19.         element.click()
  20.         self.logger.info(f"Clicked on element with locator: {locator}")
  21.    
  22.     def enter_text(self, locator, text):
  23.         element = self.wait.until(EC.visibility_of_element_located(locator))
  24.         element.clear()
  25.         element.send_keys(text)
  26.         self.logger.info(f"Entered text '{text}' in element with locator: {locator}")
  27.    
  28.     def get_text(self, locator):
  29.         element = self.wait.until(EC.visibility_of_element_located(locator))
  30.         return element.text
  31.    
  32.     def is_visible(self, locator):
  33.         try:
  34.             return self.wait.until(EC.visibility_of_element_located(locator)).is_displayed()
  35.         except:
  36.             return False
  37.    
  38.     def get_title(self):
  39.         return self.driver.title
  40.    
  41.     def get_url(self):
  42.         return self.driver.current_url
复制代码

7.3 页面类实现
  1. # pages/home_page.py
  2. from selenium.webdriver.common.by import By
  3. from pages.base_page import BasePage
  4. class HomePage(BasePage):
  5.     def __init__(self, driver):
  6.         super().__init__(driver)
  7.         self.search_input_locator = (By.ID, "search-input")
  8.         self.search_button_locator = (By.ID, "search-button")
  9.         self.login_link_locator = (By.LINK_TEXT, "Login")
  10.         self.cart_link_locator = (By.CSS_SELECTOR, ".cart-icon")
  11.    
  12.     def search_product(self, product_name):
  13.         self.enter_text(self.search_input_locator, product_name)
  14.         self.click(self.search_button_locator)
  15.         from pages.search_results_page import SearchResultsPage
  16.         return SearchResultsPage(self.driver)
  17.    
  18.     def go_to_login(self):
  19.         self.click(self.login_link_locator)
  20.         from pages.login_page import LoginPage
  21.         return LoginPage(self.driver)
  22.    
  23.     def go_to_cart(self):
  24.         self.click(self.cart_link_locator)
  25.         from pages.cart_page import CartPage
  26.         return CartPage(self.driver)
  27. # pages/login_page.py
  28. from selenium.webdriver.common.by import By
  29. from pages.base_page import BasePage
  30. class LoginPage(BasePage):
  31.     def __init__(self, driver):
  32.         super().__init__(driver)
  33.         self.username_input_locator = (By.ID, "username")
  34.         self.password_input_locator = (By.ID, "password")
  35.         self.login_button_locator = (By.ID, "login-button")
  36.         self.error_message_locator = (By.ID, "error-message")
  37.    
  38.     def login(self, username, password):
  39.         self.enter_text(self.username_input_locator, username)
  40.         self.enter_text(self.password_input_locator, password)
  41.         self.click(self.login_button_locator)
  42.         
  43.         # 根据登录结果返回不同的页面
  44.         if "dashboard" in self.driver.current_url:
  45.             from pages.dashboard_page import DashboardPage
  46.             return DashboardPage(self.driver)
  47.         else:
  48.             return self
  49.    
  50.     def get_error_message(self):
  51.         return self.get_text(self.error_message_locator)
  52.    
  53.     def is_login_successful(self):
  54.         return "dashboard" in self.driver.current_url
复制代码

7.4 测试用例实现
  1. # tests/test_login.py
  2. import pytest
  3. import csv
  4. from pages.home_page import HomePage
  5. from pages.login_page import LoginPage
  6. class TestLogin:
  7.     @pytest.mark.parametrize("username,password,expected", [
  8.         ("valid_user", "valid_password", "success"),
  9.         ("invalid_user", "invalid_password", "failure"),
  10.         ("", "valid_password", "failure"),
  11.         ("valid_user", "", "failure")
  12.     ])
  13.     def test_login(self, browser, username, password, expected):
  14.         home_page = HomePage(browser)
  15.         home_page.go_to_login()
  16.         
  17.         login_page = LoginPage(browser)
  18.         login_page.login(username, password)
  19.         
  20.         if expected == "success":
  21.             assert login_page.is_login_successful()
  22.         else:
  23.             assert not login_page.is_login_successful()
  24.             assert "Invalid username or password" in login_page.get_error_message()
  25.    
  26.     def test_login_with_csv_data(self, browser):
  27.         with open('data/login_data.csv', 'r') as file:
  28.             reader = csv.DictReader(file)
  29.             for row in reader:
  30.                 home_page = HomePage(browser)
  31.                 home_page.go_to_login()
  32.                
  33.                 login_page = LoginPage(browser)
  34.                 login_page.login(row['username'], row['password'])
  35.                
  36.                 if row['expected_result'] == 'success':
  37.                     assert login_page.is_login_successful()
  38.                 else:
  39.                     assert not login_page.is_login_successful()
  40.                     assert row['expected_message'] in login_page.get_error_message()
  41. # tests/test_add_to_cart.py
  42. import pytest
  43. from pages.home_page import HomePage
  44. from pages.product_page import ProductPage
  45. from pages.cart_page import CartPage
  46. class TestAddToCart:
  47.     def test_add_product_to_cart(self, browser):
  48.         # 搜索产品
  49.         home_page = HomePage(browser)
  50.         search_results_page = home_page.search_product("Wireless Headphones")
  51.         
  52.         # 选择第一个产品
  53.         product_page = search_results_page.select_first_product()
  54.         
  55.         # 添加到购物车
  56.         product_page.add_to_cart()
  57.         
  58.         # 验证产品已添加到购物车
  59.         assert product_page.is_success_message_displayed()
  60.         assert "Product added to cart" in product_page.get_success_message()
  61.         
  62.         # 进入购物车
  63.         cart_page = product_page.go_to_cart()
  64.         
  65.         # 验证购物车中的产品
  66.         assert cart_page.get_product_count() == 1
  67.         assert "Wireless Headphones" in cart_page.get_product_names()
  68.    
  69.     def test_add_multiple_products_to_cart(self, browser):
  70.         products = ["Wireless Mouse", "Mechanical Keyboard", "USB Cable"]
  71.         
  72.         home_page = HomePage(browser)
  73.         
  74.         for product in products:
  75.             # 搜索产品
  76.             search_results_page = home_page.search_product(product)
  77.             
  78.             # 选择第一个产品
  79.             product_page = search_results_page.select_first_product()
  80.             
  81.             # 添加到购物车
  82.             product_page.add_to_cart()
  83.             
  84.             # 验证产品已添加到购物车
  85.             assert product_page.is_success_message_displayed()
  86.             
  87.             # 返回首页继续搜索
  88.             home_page = product_page.go_home()
  89.         
  90.         # 进入购物车
  91.         cart_page = home_page.go_to_cart()
  92.         
  93.         # 验证购物车中的产品数量
  94.         assert cart_page.get_product_count() == len(products)
  95.         
  96.         # 验证所有产品都在购物车中
  97.         cart_product_names = cart_page.get_product_names()
  98.         for product in products:
  99.             assert product in cart_product_names
复制代码

7.5 测试运行器
  1. # run_tests.py
  2. import pytest
  3. import sys
  4. import os
  5. from datetime import datetime
  6. # 添加项目根目录到Python路径
  7. sys.path.append(os.path.dirname(os.path.abspath(__file__)))
  8. def main():
  9.     # 创建报告目录
  10.     report_dir = "reports"
  11.     if not os.path.exists(report_dir):
  12.         os.makedirs(report_dir)
  13.    
  14.     # 生成报告文件名
  15.     timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  16.     report_file = os.path.join(report_dir, f"test_report_{timestamp}.html")
  17.    
  18.     # 运行测试并生成HTML报告
  19.     pytest.main([
  20.         "--html=" + report_file,
  21.         "--self-contained-html",
  22.         "-v",
  23.         "tests/"
  24.     ])
  25. if __name__ == "__main__":
  26.     main()
复制代码

8. 常见问题与解决方案

8.1 元素定位失败

问题:无法定位页面元素,抛出NoSuchElementException异常。

可能原因:

1. 元素ID或定位器不正确
2. 元素尚未加载完成
3. 元素在iframe中
4. 元素被其他元素遮挡

解决方案:
  1. # 1. 检查定位器是否正确
  2. # 使用浏览器开发者工具验证定位器
  3. # 2. 使用显式等待等待元素加载
  4. from selenium.webdriver.support.ui import WebDriverWait
  5. from selenium.webdriver.support import expected_conditions as EC
  6. wait = WebDriverWait(driver, 10)
  7. element = wait.until(EC.presence_of_element_located((By.ID, "element-id")))
  8. # 3. 处理iframe中的元素
  9. driver.switch_to.frame("iframe-name")
  10. element = driver.find_element(By.ID, "element-id")
  11. driver.switch_to.default_content()
  12. # 4. 使用JavaScript点击元素
  13. element = driver.find_element(By.ID, "element-id")
  14. driver.execute_script("arguments[0].click();", element)
复制代码

8.2 页面加载超时

问题:页面加载时间过长,导致脚本超时。

解决方案:
  1. # 设置页面加载超时时间
  2. driver.set_page_load_timeout(30)  # 30秒
  3. # 使用try-except处理超时异常
  4. from selenium.common.exceptions import TimeoutException
  5. try:
  6.     driver.get("https://example.com")
  7. except TimeoutException:
  8.     print("页面加载超时")
  9.     # 执行其他操作,如刷新页面或记录错误
  10.     driver.execute_script("window.stop();")  # 停止页面加载
复制代码

8.3 动态元素处理

问题:页面元素是动态生成的,无法直接定位。

解决方案:
  1. # 1. 使用显式等待等待元素出现
  2. wait = WebDriverWait(driver, 10)
  3. element = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".dynamic-element")))
  4. # 2. 使用更灵活的XPath或CSS选择器
  5. # 例如,选择包含特定文本的元素
  6. element = driver.find_element(By.XPATH, "//*[contains(text(), 'Dynamic Content')]")
  7. # 3. 使用JavaScript获取元素
  8. element = driver.execute_script("return document.querySelector('.dynamic-element');")
  9. # 4. 遍历父元素查找子元素
  10. parent_element = driver.find_element(By.ID, "parent-container")
  11. child_elements = parent_element.find_elements(By.TAG_NAME, "div")
  12. for element in child_elements:
  13.     if "dynamic-class" in element.get_attribute("class"):
  14.         # 找到目标元素
  15.         break
复制代码

8.4 验证码处理

问题:网站使用验证码防止自动化操作。

解决方案:
  1. # 1. 测试环境中禁用验证码
  2. # 联系开发人员在测试环境中提供禁用验证码的选项
  3. # 2. 使用固定的测试验证码
  4. # 在测试环境中使用固定的验证码,如"1234"
  5. # 3. 使用第三方验证码识别服务
  6. # 例如,使用Tesseract OCR或在线API识别验证码
  7. import pytesseract
  8. from PIL import Image
  9. # 截取验证码图片
  10. captcha_element = driver.find_element(By.ID, "captcha-image")
  11. captcha_element.screenshot("captcha.png")
  12. # 使用OCR识别验证码
  13. image = Image.open("captcha.png")
  14. captcha_text = pytesseract.image_to_string(image)
  15. # 输入验证码
  16. driver.find_element(By.ID, "captcha-input").send_keys(captcha_text)
  17. # 4. 手动输入验证码
  18. # 在自动化测试过程中暂停,等待手动输入验证码
  19. input("请手动输入验证码并按回车继续...")
复制代码

8.5 浏览器兼容性问题

问题:脚本在不同浏览器中表现不一致。

解决方案:
  1. # 1. 使用WebDriverWait和显式等待,减少硬编码等待
  2. # 这有助于处理不同浏览器的加载速度差异
  3. # 2. 针对不同浏览器使用不同的定位策略
  4. # 例如,某些浏览器可能对XPath的支持更好
  5. try:
  6.     # 尝试使用CSS选择器
  7.     element = driver.find_element(By.CSS_SELECTOR, "#my-element")
  8. except:
  9.     # 如果失败,尝试使用XPath
  10.     element = driver.find_element(By.XPATH, "//*[@id='my-element']")
  11. # 3. 使用浏览器特定的选项
  12. # Chrome选项
  13. from selenium.webdriver.chrome.options import Options
  14. chrome_options = Options()
  15. chrome_options.add_argument("--disable-extensions")
  16. chrome_options.add_argument("--disable-infobars")
  17. driver = webdriver.Chrome(options=chrome_options)
  18. # Firefox选项
  19. from selenium.webdriver.firefox.options import Options
  20. firefox_options = Options()
  21. firefox_options.add_argument("--disable-extensions")
  22. driver = webdriver.Firefox(options=firefox_options)
  23. # 4. 使用浏览器特定的等待时间
  24. # 根据浏览器类型调整等待时间
  25. if isinstance(driver, webdriver.Chrome):
  26.     wait_time = 10
  27. elif isinstance(driver, webdriver.Firefox):
  28.     wait_time = 15
  29. else:
  30.     wait_time = 10
  31. wait = WebDriverWait(driver, wait_time)
复制代码

9. 总结与展望

9.1 总结

本文全面解析了Selenium自动化网页操作,从基础语法到高级应用实战。我们学习了:

1. Selenium的基础概念和环境搭建
2. 元素定位的各种方法和策略
3. 页面元素的基本操作和高级交互
4. 等待机制的重要性和实现方式
5. 处理弹窗、多窗口、iframe等复杂场景
6. Page Object模式和其他设计模式的应用
7. 数据驱动测试和测试框架集成
8. 实战案例:电商网站自动化测试
9. 常见问题与解决方案

通过这些知识,您可以构建强大、可维护的Web自动化测试框架,提高测试效率,减少人工测试的工作量。

9.2 最佳实践

在使用Selenium进行自动化测试时,以下是一些最佳实践:

1. 使用Page Object模式:将页面元素和操作封装成类,提高代码的可维护性和可读性。
2. 合理使用等待机制:避免使用time.sleep(),优先使用显式等待。
3. 选择稳定的元素定位器:优先使用ID,其次是Name、CSS选择器和XPath。
4. 实现数据驱动测试:将测试数据与测试逻辑分离,提高测试的灵活性。
5. 添加日志和截图:在关键步骤添加日志记录,失败时自动截图,便于问题定位。
6. 使用配置文件:将环境配置、测试参数等放在配置文件中,便于管理和修改。
7. 实现异常处理:添加适当的异常处理,提高脚本的健壮性。
8. 定期维护测试脚本:随着应用的更新,定期检查和更新测试脚本。

使用Page Object模式:将页面元素和操作封装成类,提高代码的可维护性和可读性。

合理使用等待机制:避免使用time.sleep(),优先使用显式等待。

选择稳定的元素定位器:优先使用ID,其次是Name、CSS选择器和XPath。

实现数据驱动测试:将测试数据与测试逻辑分离,提高测试的灵活性。

添加日志和截图:在关键步骤添加日志记录,失败时自动截图,便于问题定位。

使用配置文件:将环境配置、测试参数等放在配置文件中,便于管理和修改。

实现异常处理:添加适当的异常处理,提高脚本的健壮性。

定期维护测试脚本:随着应用的更新,定期检查和更新测试脚本。

9.3 未来展望

Selenium和Web自动化测试领域正在不断发展,以下是一些未来趋势:

1. AI辅助测试:人工智能技术将被用于自动生成测试用例、预测测试结果和优化测试流程。
2. 视觉测试:除了功能测试,视觉测试将变得更加重要,确保UI在不同设备和浏览器上的一致性。
3. 无头浏览器的普及:无头浏览器(如Headless Chrome)将在CI/CD流程中更广泛地使用。
4. 移动端测试整合:Web自动化测试将与移动端测试(如Appium)更好地整合。
5. 云端测试服务:基于云的测试服务(如BrowserStack、Sauce Labs)将提供更强大的跨浏览器测试能力。
6. 与DevOps的深度集成:自动化测试将更深入地集成到DevOps流程中,实现持续测试。

AI辅助测试:人工智能技术将被用于自动生成测试用例、预测测试结果和优化测试流程。

视觉测试:除了功能测试,视觉测试将变得更加重要,确保UI在不同设备和浏览器上的一致性。

无头浏览器的普及:无头浏览器(如Headless Chrome)将在CI/CD流程中更广泛地使用。

移动端测试整合:Web自动化测试将与移动端测试(如Appium)更好地整合。

云端测试服务:基于云的测试服务(如BrowserStack、Sauce Labs)将提供更强大的跨浏览器测试能力。

与DevOps的深度集成:自动化测试将更深入地集成到DevOps流程中,实现持续测试。

随着这些趋势的发展,Selenium作为Web自动化测试的核心工具,将继续发挥重要作用,并与其他工具和技术结合,提供更全面、更高效的测试解决方案。

通过掌握Selenium及其相关技术,您将能够在Web自动化测试领域保持竞争力,并为软件质量保障做出重要贡献。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

加入Discord频道

加入Discord频道

加入QQ社群

加入QQ社群

联系我们|小黑屋|TG频道|RSS |网站地图

Powered by Pixtech

© 2025-2026 Pixtech Team.