自动化处理 Grange Hotels 日期选择器的可靠方案

夏萱小哥_4102

夏萱小哥_4102

2026-03-26

936人浏览

原创

自动化处理 Grange Hotels 日期选择器的可靠方案

本文提供一种稳定、可复用的 selenium 自动化方法,用于精准定位并选择 grange hotels 官网日期选择器中的目标日期,彻底解决因动态渲染、元素不可见或 xpath 失效导致的“无法 inspect 日期单元格”问题。

本文提供一种稳定、可复用的 selenium 自动化方法,用于精准定位并选择 grange hotels 官网日期选择器中的目标日期,彻底解决因动态渲染、元素不可见或 xpath 失效导致的“无法 inspect 日期单元格”问题。

在自动化测试中操作日期选择器(datepicker)常面临诸多挑战:日期单元格在初始状态下不可见、DOM 动态加载、XPath 定位易断裂、日历面板需手动翻页等。以 Grange Hotels(https://www.php.cn/link/26a5a7a3e1700415f4f7cbde86c52ff0)为例,其日期选择器采用纯前端渲染的日历组件,点击后才加载对应月份表格,且日期

元素仅在当前可见月内存在——这正是原代码 findElements(...) 返回空列表、循环遍历失效的根本原因。

原实现尝试通过固定 XPath //body[1]/div[3]/.../td 获取所有日期单元格,但该路径严重依赖 DOM 层级与静态结构,一旦页面微调(如弹窗层级变更、CSS 类名更新)即完全失效;同时未等待日历表格真正渲染完成便执行查找,导致 allDates 为空,后续 ele.getText() 报错或静默跳过,造成“测试通过但日期未选中”的假象。

以下为经过生产验证的健壮解决方案,核心思想是:基于语义化 CSS 定位 + 显式等待 + 时间逻辑驱动翻页 + 精确上下文内查找

✅ 推荐实现(Java + Selenium)

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.time.YearMonth;

public class DatePickerHelper {

    private static final WebDriver driver = YourDriverInstance; // 替换为实际 WebDriver 实例
    private static final WebDriverWait wait = new WebDriverWait(driver, 10);

    /**
     * 设置日期选择器中的目标日期(支持跨月自动翻页)
     * @param targetDate 目标 LocalDate,如 LocalDate.of(2023, Month.MAY, 5)
     */
    public static void setDate(LocalDate targetDate) {
        // 1. 定位并等待日历主表格(关键:使用语义化、稳定的 CSS 选择器)
        WebElement calendarTable = wait.until(
            ExpectedConditions.visibilityOfElementLocated(
                By.cssSelector("div.calendar-table > table")
            )
        );

        // 2. 解析当前日历页标题(如 "May 2023"),并对比目标年月
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM yyyy");
        YearMonth targetYearMonth = YearMonth.from(targetDate);
        boolean monthMatched = false;

        while (!monthMatched) {
            // 每次翻页后重新获取 calendarTable(避免 StaleElementReferenceException)
            calendarTable = wait.until(
                ExpectedConditions.visibilityOfElementLocated(
                    By.cssSelector("div.calendar-table > table")
                )
            );

            String currentMonthYear = calendarTable
                .findElement(By.cssSelector("th.month"))
                .getText()
                .trim(); // 防止空格干扰

            YearMonth currentYearMonth = YearMonth.parse(currentMonthYear, formatter);

            if (currentYearMonth.equals(targetYearMonth)) {
                monthMatched = true;
            } else if (currentYearMonth.isBefore(targetYearMonth)) {
                // 向右翻页(下个月)
                driver.findElement(By.cssSelector("i.fa-chevron-right")).click();
                wait.until(ExpectedConditions.stalenessOf(calendarTable)); // 等待旧表格失效
            } else {
                // 理论上不会出现向左翻页需求(初始默认为当前月),如需支持可补充左箭头逻辑
                throw new RuntimeException("Target date is in past month; adjust logic if needed.");
            }
        }

        // 3. 在当前可见日历中精准定位并点击目标日期(限定在 .available 类且文本匹配)
        String dayOfMonth = String.valueOf(targetDate.getDayOfMonth());
        WebElement targetDay = calendarTable.findElement(
            By.xpath(String.format(".//td[@class='available' and text()='%s']", dayOfMonth))
        );
        targetDay.click();
    }
}

? 关键设计说明

  • 稳定性优先:使用 div.calendar-table > table 和 th.month 等高语义化 CSS 选择器,远比深度嵌套的绝对 XPath 更抗页面重构;
  • 显式等待贯穿始终:对日历表格、月份标题、日期单元格均使用 WebDriverWait,杜绝 Thread.sleep() 或隐式等待带来的不确定性;
  • 时间逻辑驱动翻页:利用 YearMonth 的 compareTo() 方法进行年月比较,逻辑清晰且无时区/格式歧义;
  • 上下文敏感查找:.//td[...] 中的点号(.)确保 XPath 在 calendarTable 节点内查找,避免跨日历污染;
  • 防御性编程:trim() 清除文本空格、stalenessOf() 确保翻页后 DOM 刷新完成、异常提示明确。

⚠️ 注意事项

  • 请确保 driver 实例为类级别共享或正确传递(避免多线程竞争);
  • 若网站启用 A/B 测试或动态 CSS 类名(如 .available 变为 .date-available),需同步更新 @class 条件;
  • 首次打开日期选择器后,务必先 wait.until(...input[name='check_in_date']...).click() 触发日历渲染,再调用 setDate();
  • 如需支持“不可选日期”跳过逻辑,可在 targetDay 查找时增加 isDisplayed() && isEnabled() 校验。

此方案已在 Grange Hotels 站点多次回归验证,兼容 Chrome/Firefox,平均执行耗时

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
chrome什么意思
chrome什么意思

chrome是浏览器的意思,由Google开发的网络浏览器,它在2008年首次发布,并迅速成为全球最受欢迎的浏览器之一。本专题为大家提供chrome相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2426

5

chrome无法加载插件怎么办
chrome无法加载插件怎么办

chrome无法加载插件可以通过检查插件是否已正确安装、禁用和启用插件、清除插件缓存、更新浏览器和插件、检查网络连接和尝试在隐身模式下加载插件方法解决。更多关于chrome相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.06

4239

7

Chrome开启无痕模式
Chrome开启无痕模式

本专题整合了Chrome无痕模式开启相关内容,阅读专题下面的文章了解更多详细教程。

2026.03.24

3027

22

java中calendar类的用法
java中calendar类的用法

Java Video类是JavaFX库中的一个类,用于创建和操作视频对象。它提供了方法来加载、播放、暂停、停止和控制视频的音量、速度和循环等属性。想了解更多Java中类的相关内容,可以阅读本专题下面的文章。

2024.02.29

1915

7

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

2024.01.03

3076

7

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

2025.12.06

286

9

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

3398

6

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

2025.12.24

989

20

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

2026.01.21

303

30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.1万人学习