selenium设置等待的几种方式

一、强制等待

Java 原生的线程休眠方法,不属于 Selenium API。

try {
    Thread.sleep(2000); // 强制暂停 2 秒
} catch (InterruptedException e) {
    e.printStackTrace();
}

‌缺点:‌

  • ‌效率低‌:即使元素在第 1 秒就加载好了,也必须等满 2 秒。
  • ‌不稳定‌:如果网络波动导致加载超过 2 秒,脚本会失败;如果加载很快,则浪费时间。
  • ‌适用场景‌:仅建议在调试脚本或处理极少数无法通过常规等待解决的异步问题时临时使用。

二、隐式等待 (Implicit Wait)

隐式等待告诉 WebDriver 在查找元素时,如果元素没有立即出现,就轮询 DOM 一段时间。默认设置为 0,意味着一旦找不到元素就立即抛出异常

// 设置全局隐式等待时间为 10 秒
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

// 此后所有的 findElement 操作都会最多等待 10 秒
WebElement elem = driver.findElement(By.id("kw")); 

注意事项:‌

  • ‌全局生效‌:设置后对当前 Driver 实例的所有元素查找生效。
  • ‌局限性‌:它只等待元素出现在 DOM 中,不保证元素可见或可交互。如果元素存在但被遮挡,隐式等待不会报错,但后续点击可能会失败。
  • ‌不建议与显式等待混用‌:混合使用可能导致不可预测的等待时间(通常取两者最大值),建议优先使用显式等待。

三、显式等待 (Explicit Wait)

显式等待允许你定义一个等待条件,直到该条件成立或超时才继续执行。它不会盲目等待最大时间,一旦条件满足立即向下执行。

// 初始化等待对象,最多等待 10 秒
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

// 场景1:等待元素可见 (Visibility)
WebElement elem = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("kw")));

// 场景2:等待元素可点击 (Clickable)
wait.until(ExpectedConditions.elementToBeClickable(By.id("submit"))).click();

// 场景3:等待元素存在于 DOM 中 (Presence)
wait.until(ExpectedConditions.presenceOfElementLocated(By.className("result")));

// 场景4:等待页面标题包含特定文本
wait.until(ExpectedConditions.titleContains("百度"));

// 场景5:等待新窗口出现
wait.until(ExpectedConditions.numberOfWindowsToBe(2));

常用 ExpectedConditions (预期条件):‌

  • visibilityOfElementLocated: 元素存在且可见。
  • elementToBeClickable: 元素存在、可见且未被禁用。
  • presenceOfElementLocated: 元素存在于 DOM 中(不一定可见)。
  • textToBePresentInElement: 元素中包含指定文本。
  • frameToBeAvailableAndSwitchToIt: Frame 可用并自动切换。
  • alertIsPresent: 弹窗出现。

来看看selenium Java源码

public class WebDriverWait extends FluentWait<WebDriver> {
    private final WebDriver driver;

    public WebDriverWait(WebDriver driver, Duration timeout) {
        this(driver, timeout, Duration.ofMillis(500L), Clock.systemDefaultZone(), Sleeper.SYSTEM_SLEEPER);
    }

    public WebDriverWait(WebDriver driver, Duration timeout, Duration sleep) {
        this(driver, timeout, sleep, Clock.systemDefaultZone(), Sleeper.SYSTEM_SLEEPER);
    }

    public WebDriverWait(WebDriver driver, Duration timeout, Duration sleep, Clock clock, Sleeper sleeper) {
        super(driver, clock, sleeper);
        this.withTimeout(timeout);
        this.pollingEvery(sleep);
        this.ignoring(NotFoundException.class);
        this.driver = driver;
    }

    protected RuntimeException timeoutException(String message, Throwable lastException) {
        WebDriver exceptionDriver = this.driver;
        TimeoutException ex = new TimeoutException(message, lastException);
        ex.addInfo("Driver info", exceptionDriver.getClass().getName());

        while(exceptionDriver instanceof WrapsDriver) {
            exceptionDriver = ((WrapsDriver)exceptionDriver).getWrappedDriver();
        }

        if (exceptionDriver instanceof RemoteWebDriver) {
            RemoteWebDriver remote = (RemoteWebDriver)exceptionDriver;
            if (remote.getSessionId() != null) {
                ex.addInfo("Session ID", remote.getSessionId().toString());
            }

            if (remote.getCapabilities() != null) {
                ex.addInfo("Capabilities", remote.getCapabilities().toString());
            }
        }

        throw ex;
    }
}

首先WebDriverWait继承自FluentWait这个类,我们来看看FluentWait的until这个方法

public <V> V until(Function<? super T, V> isTrue) {
        Instant end = this.clock.instant().plus(this.timeout);

        while(true) {
            Throwable lastException;
            try {
                V value = isTrue.apply(this.input);
                if (value != null && (Boolean.class != value.getClass() || Boolean.TRUE.equals(value))) {
                    return value;
                }

                lastException = null;
            } catch (Throwable var7) {
                lastException = this.propagateIfNotIgnored(var7);
            }

            if (end.isBefore(this.clock.instant())) {
                String message = this.messageSupplier != null ? (String)this.messageSupplier.get() : null;
                String timeoutMessage = String.format("Expected condition failed: %s%n(tried for %s with %d milliseconds interval)", message == null ? "waiting for " + String.valueOf(isTrue) : message, formatTimeout(this.timeout), this.interval.toMillis());
                throw this.timeoutException(timeoutMessage, lastException);
            }

            try {
                this.sleeper.sleep(this.interval);
            } catch (InterruptedException var6) {
                Thread.currentThread().interrupt();
                throw new WebDriverException(var6);
            }
        }
    }

Function<? super T, V>是个函数式接口
然后接口ExpectedCondition又继承Function

public interface ExpectedCondition<T> extends Function<WebDriver, T>, java.util.function.Function<WebDriver, T> {
}

最后ExpectedConditions这个类中写了很多ExpectedCondition的实现方法,比如

    public static ExpectedCondition<@Nullable WebElement> presenceOfElementLocated(final By locator) {
        return new ExpectedCondition<WebElement>() {
            public @Nullable WebElement apply(WebDriver driver) {
                try {
                    return driver.findElement(locator);
                } catch (NoSuchElementException var3) {
                    return null;
                }
            }

            public String toString() {
                return "presence of element found by " + String.valueOf(locator);
            }
        };
    }

所以在实际运用中可以根据实际情况来自己实现ExpectedCondition接口
比如

import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;

public class TextToBePresentInElement implements ExpectedCondition<WebElement> {
    private By locator;
    private String expectedText;

    public TextToBePresentInElement(By locator, String expectedText) {
        this.locator = locator;
        this.expectedText = expectedText;
    }

    @Override
    public WebElement apply(WebDriver driver) {
        try {
            WebElement element = driver.findElement(locator);
            if (element.getText().contains(expectedText)) {
                return element;
            }
            return null;
        } catch (Exception e) {
            return null; // 元素未找到或异常,继续等待
        }
    }
}

// 使用方式
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement elem = wait.until(new TextToBePresentInElement(By.id("msg"), "加载完成"));

四、自定义等待 (‌FluentWait‌ )

  • ‌自定义轮询间隔‌:默认每 500ms 检查一次,可调整为更短(如 100ms)或更长。
  • ‌忽略异常‌:可指定在等待期间忽略哪些异常(如 NoSuchElementException),避免脚本因短暂缺失而中断。
  • ‌超时设置‌:设定最大等待时长,超时后抛出 TimeoutException。

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.FluentWait;
import java.time.Duration;
import java.util.function.Function;
import org.openqa.selenium.NoSuchElementException;

// 1. 创建 FluentWait 实例
FluentWait<WebDriver> wait = new FluentWait<>(driver)
    .withTimeout(Duration.ofSeconds(30))   // 最大等待 30 秒
    .pollingEvery(Duration.ofMillis(500))  // 每 500 毫秒检查一次
    .ignoring(NoSuchElementException.class); // 忽略元素未找到异常

// 2. 定义等待条件 (使用 Lambda 表达式)
WebElement element = wait.until(new Function<WebDriver, WebElement>() {
    @Override
    public WebElement apply(WebDriver driver) {
        return driver.findElement(By.id("dynamicElement"));
    }
});

// 简化写法 (Lambda)
WebElement elem = wait.until(driver -> driver.findElement(By.id("dynamicElement")));
posted @ 2026-06-25 09:57  alex_lau  阅读(10)  评论(0)    收藏  举报