Selenium自动化实战:从小白到大神的进阶之路

197 阅读7分钟

Selenium自动化实战:从小白到大神的进阶之路

今天咱们来聊聊 Selenium自动化测试 那些事儿。

说实话,我第一次接触Selenium的时候也是一脸懵,啥元素定位、显式等待、隐式等待...听得我脑袋瓜子嗡嗡的!

但别怕!跟着我一步步来,保证你也能玩转Selenium!

一、精准元素定位技巧

元素定位图片

元素定位图片

定位元素就像在人山人海中找到你的好朋友,得有独特的特征才行!

最常用的定位方法有这几种:

# 导入必要的库
from selenium import webdriver
from selenium.webdriver.common.by import By

# 初始化浏览器驱动
driver = webdriver.Chrome()
driver.get("https://www.example.com")

# 1. ID定位(最靠谱的方式,就像身份证号,独一无二)
username_input = driver.find_element(By.ID, "username")

# 2. NAME定位(常用于表单元素)
password_input = driver.find_element(By.NAME, "password")

# 3. CLASS_NAME定位(适合样式相同的元素)
buttons = driver.find_elements(By.CLASS_NAME, "btn-primary")

# 4. XPATH定位(最强大但也最复杂,就像给地址导航)
login_button = driver.find_element(By.XPATH, "//button[@type='submit' and contains(text(), '登录')]")

# 5. CSS选择器(前端开发者的最爱,简洁高效)
forgot_password = driver.find_element(By.CSS_SELECTOR, ".forgot-password a")

小技巧: 优先使用ID、NAME这种稳定的属性,实在不行再用XPATH或CSS选择器。

踩坑警告: 有些网站的元素ID或CLASS是动态生成的(比如id="item_7a2b3c"),每次刷新都变,这时候就得找更稳定的特征!

二、智能等待机制解析

等待机制图片

等待机制图片

网页加载需要时间,别着急,学会"等待"!

不会等待的自动化脚本 = 定时炸弹! 随时可能因为元素还没加载出来就崩溃!

三种等待方式,你必须掌握:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# 1. 强制等待(最简单粗暴,但效率最低)
time.sleep(3)  # 无脑等3秒,不管元素是否已经出现

# 2. 隐式等待(全局设置,影响所有find_element操作)
driver.implicitly_wait(10)  # 最多等10秒,元素出现就继续

# 3. 显式等待(最智能,只针对特定元素等待)
wait = WebDriverWait(driver, 10)  # 创建等待对象,超时时间10秒
login_button = wait.until(EC.element_to_be_clickable((By.ID, "login")))
# 等待直到ID为login的按钮可点击,最多等10秒

实战建议: 显式等待是最佳选择!可以精确控制等待条件,比如等元素可见、可点击、文本包含特定内容等。

等待条件花式玩法:

# 等待元素可见
wait.until(EC.visibility_of_element_located((By.ID"result")))

# 等待元素不可见(适合等待加载动画消失)
wait.until(EC.invisibility_of_element_located((By.CLASS_NAME"loading")))

# 等待页面标题包含特定文本
wait.until(EC.title_contains("登录成功"))

# 等待特定文本出现在页面中
wait.until(EC.text_to_be_present_in_element((By.ID"message"), "操作成功"))

三、复杂用户交互模拟

用户交互图片

用户交互图片

不只是点点点,Selenium能模拟各种骚操作!

from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

# 基本操作:点击、输入、清除
button = driver.find_element(By.ID, "submit")
button.click()

input_field = driver.find_element(By.NAME, "search")
input_field.clear()  # 清除已有内容
input_field.send_keys("Selenium自动化")  # 输入文本
input_field.send_keys(Keys.ENTER)  # 按回车键

# 高级操作:鼠标悬停、拖拽、右键点击
actions = ActionChains(driver)

# 鼠标悬停(展开下拉菜单)
menu = driver.find_element(By.CLASS_NAME, "dropdown")
actions.move_to_element(menu).perform()

# 拖拽操作(常用于滑块验证码)
source = driver.find_element(By.ID, "draggable")
target = driver.find_element(By.ID, "droppable")
actions.drag_and_drop(source, target).perform()

# 模拟组合键(Ctrl+A全选)
actions.key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()

# 处理弹窗
alert = driver.switch_to.alert
print(alert.text)  # 获取弹窗文本
alert.accept()  # 点击"确定"
# alert.dismiss()  # 点击"取消"
# alert.send_keys("输入内容")  # 在提示框中输入

处理iframe嵌套页面:

# 切换到iframe内部(很多网站的富文本编辑器都在iframe中)
iframe = driver.find_element(By.ID, "editor-frame")
driver.switch_to.frame(iframe)

# 在iframe中操作元素
editor = driver.find_element(By.CLASS_NAME, "editor")
editor.send_keys("这是在iframe中输入的内容")

# 切回主文档
driver.switch_to.default_content()

文件上传小技巧: 直接给input[type="file"]元素发送文件路径,不需要点击浏览按钮!

file_input = driver.find_element(By.ID, "upload")
file_input.send_keys("C:\Users\YourName\Pictures\test.jpg")

四、多浏览器兼容方案

多浏览器图片

多浏览器图片

一套代码,多浏览器运行,这才是真正的自动化!

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.edge.service import Service as EdgeService
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
from webdriver_manager.microsoft import EdgeChromiumDriverManager

# 自动下载并配置对应的驱动程序
def get_driver(browser_name):
    browser_name = browser_name.lower()
    if browser_name == "chrome":
        service = ChromeService(ChromeDriverManager().install())
        return webdriver.Chrome(service=service)
    elif browser_name == "firefox":
        service = FirefoxService(GeckoDriverManager().install())
        return webdriver.Firefox(service=service)
    elif browser_name == "edge":
        service = EdgeService(EdgeChromiumDriverManager().install())
        return webdriver.Edge(service=service)
    else:
        raise ValueError(f"不支持的浏览器: {browser_name}")

# 使用方式
driver = get_driver("chrome")  # 可以轻松切换为 "firefox" 或 "edge"

无头模式(Headless): 浏览器在后台运行,不显示界面,适合服务器环境!

from selenium.webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument("--headless")  # 启用无头模式
chrome_options.add_argument("--disable-gpu")  # 某些系统需要
chrome_options.add_argument("--window-size=1920,1080")  # 设置窗口大小

driver = webdriver.Chrome(options=chrome_options)

跨浏览器兼容性提示: 不同浏览器对CSS选择器和JavaScript的支持可能有差异,测试时最好覆盖多个主流浏览器。

五、测试框架集成秘籍

测试框架图片

测试框架图片

单打独斗不如组团作战,Selenium + 测试框架才是王道!

与Pytest集成

import pytest
from selenium import webdriver

# 使用fixture在测试前后自动处理driver的创建和关闭
@pytest.fixture
def driver():
    # 测试前创建driver
    driver = webdriver.Chrome()
    driver.maximize_window()
    yield driver  # 返回driver给测试函数使用
    # 测试后关闭driver
    driver.quit()

# 测试登录功能
def test_login(driver):
    driver.get("https://example.com/login")
    
    # 输入用户名密码
    driver.find_element(By.ID, "username").send_keys("testuser")
    driver.find_element(By.ID, "password").send_keys("password123")
    driver.find_element(By.ID, "login-button").click()
    
    # 使用断言验证登录成功
    wait = WebDriverWait(driver, 10)
    welcome_message = wait.until(EC.visibility_of_element_located((By.ID, "welcome")))
    assert "欢迎回来" in welcome_message.text

页面对象模式(POM): 把页面操作封装成类,让测试代码更清晰!

# 登录页面类
class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        self.username_input = (By.ID, "username")
        self.password_input = (By.ID, "password")
        self.login_button = (By.ID, "login-button")
    
    def navigate(self):
        self.driver.get("https://example.com/login")
        return self
    
    def login(self, username, password):
        self.driver.find_element(*self.username_input).send_keys(username)
        self.driver.find_element(*self.password_input).send_keys(password)
        self.driver.find_element(*self.login_button).click()
        return HomePage(self.driver)  # 登录成功后返回首页对象

# 首页类
class HomePage:
    def __init__(self, driver):
        self.driver = driver
        self.welcome_message = (By.ID, "welcome")
    
    def get_welcome_text(self):
        wait = WebDriverWait(self.driver, 10)
        element = wait.until(EC.visibility_of_element_located(self.welcome_message))
        return element.text

# 使用POM的测试代码
def test_login_with_pom(driver):
    home_page = LoginPage(driver).navigate().login("testuser""password123")
    assert "欢迎回来" in home_page.get_welcome_text()

自动截图和日志记录: 测试失败时自动保存证据,排查问题事半功倍!

import logging
import os
from datetime import datetime

# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 失败时截图的函数
def take_screenshot(driver, name):
    os.makedirs("screenshots", exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    screenshot_path = f"screenshots/{name}_{timestamp}.png"
    driver.save_screenshot(screenshot_path)
    logger.info(f"截图保存在: {screenshot_path}")
    return screenshot_path

# 在pytest中使用
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    report = outcome.get_result()
    if report.when == "call" and report.failed:
        driver = item.funcargs.get("driver")
        if driver:
            take_screenshot(driver, f"failure_{item.name}")

实战完整示例:自动化登录并提取数据

整合所有技巧,完成一个真实场景的自动化脚本!

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import pandas as pd
import time
import logging

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class WebScraper:
    def __init__(self, headless=False):
        options = webdriver.ChromeOptions()
        if headless:
            options.add_argument("--headless")
            options.add_argument("--disable-gpu")
            options.add_argument("--window-size=1920,1080")
        
        service = Service()
        self.driver = webdriver.Chrome(service=service, options=options)
        self.wait = WebDriverWait(self.driver, 10)
        logger.info("浏览器初始化完成")
    
    def login(self, url, username, password):
        try:
            logger.info(f"正在访问: {url}")
            self.driver.get(url)
            
            # 等待登录表单加载
            username_input = self.wait.until(EC.presence_of_element_located((By.ID, "username")))
            password_input = self.driver.find_element(By.ID, "password")
            login_button = self.driver.find_element(By.XPATH, "//button[contains(text(), '登录')]")
            
            # 输入登录信息
            username_input.send_keys(username)
            password_input.send_keys(password)
            login_button.click()
            
            # 验证登录成功
            self.wait.until(EC.url_contains("dashboard"))
            logger.info("登录成功!")
            return True
        except TimeoutException:
            logger.error("登录失败: 页面加载超时")
            self.driver.save_screenshot("login_error.png")
            return False
        except Exception as e:
            logger.error(f"登录过程中出错: {str(e)}")
            self.driver.save_screenshot("login_error.png")
            return False
    
    def extract_table_data(self, table_selector):
        try:
            # 等待表格加载
            table = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, table_selector)))
            
            # 提取表头
            headers = []
            header_cells = table.find_elements(By.CSS_SELECTOR, "thead th")
            for cell in header_cells:
                headers.append(cell.text.strip())
            
            # 提取数据行
            rows = []
            data_rows = table.find_elements(By.CSS_SELECTOR, "tbody tr")
            for row in data_rows:
                cells = row.find_elements(By.TAG_NAME, "td")
                row_data = [cell.text.strip() for cell in cells]
                rows.append(row_data)
            
            # 创建DataFrame
            df = pd.DataFrame(rows, columns=headers)
            logger.info(f"成功提取{len(rows)}行数据")
            return df
        except Exception as e:
            logger.error(f"提取表格数据时出错: {str(e)}")
            self.driver.save_screenshot("table_extract_error.png")
            return None
    
    def export_to_excel(self, df, filename):
        try:
            df.to_excel(filename, index=False)
            logger.info(f"数据已导出到: {filename}")
            return True
        except Exception as e:
            logger.error(f"导出Excel时出错: {str(e)}")
            return False
    
    def close(self):
        if self.driver:
            self.driver.quit()
            logger.info("浏览器已关闭")

# 使用示例
if __name__ == "__main__":
    scraper = WebScraper(headless=False)
    try:
        if scraper.login("https://example.com/login""your_username""your_password"):
            # 等待页面加载完成
            time.sleep(2)
            
            # 导航到数据页面
            scraper.driver.get("https://example.com/data")
            
            # 提取表格数据
            data = scraper.extract_table_data("#data-table")
            if data is not None:
                # 导出到Excel
                scraper.export_to_excel(data, "extracted_data.xlsx")
    finally:
        scraper.close()

总结一下: Selenium自动化测试不难,关键是掌握这几点:

  1. 精准定位元素 - 找对人才能办对事
  2. 智能等待机制 - 别急,给网页一点加载时间
  3. 模拟复杂交互 - 不只是点点点,还能拖拽、悬停
  4. 多浏览器兼容 - 一套代码,到处运行
  5. 测试框架集成 - 规范化你的测试,事半功倍

掌握了这些,你就从Selenium小白变成大神啦!有问题随时交流,一起进步!

本文使用 markdown.com.cn 排版