如何处理phptravels.net网站的日历控件?如何编写通用处理函数?
如何处理phptravels.net网站的日历控件?如何编写通用处理函数?
兄弟,我之前也折腾过这个网站的酒店日历控件,结合你用的Java Selenium和Page Object Model(POM),给你分享一套实用的解决方案,还能写出通用的日历处理函数,以后换其他类似日历也能复用!
先理清楚这个日历的结构特点
phptravels的酒店日历是典型的「月份面板切换+日期点击」类型:点击入住/退房输入框会弹出日历面板,面板顶部显示当前年月,左右有切换月份的箭头,日期是按天排列的单元格,部分过期日期会置灰不可选。
基于POM的分步实现方案
我们把所有日历操作封装在你的HomePage类里,拆分几个核心方法,再组合成完整的日期选择流程:
1. 封装打开日历的方法
首先要触发日历面板显示,定位入住日期的输入框或者触发按钮(这个网站里点击输入框就会弹出日历):
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class HomePage { private WebDriver driver; // 定位入住日期输入框 @FindBy(id = "checkin") private WebElement checkinInput; public HomePage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } // 打开入住日历面板 public void openCheckinCalendar() { // 先点击输入框触发日历弹出,避免直接操作元素找不到 checkinInput.click(); } }
2. 编写通用的「月份切换」函数
这是日历处理的核心难点:要自动切换到目标日期所在的月份。我们可以通过对比当前日历显示的年月和目标年月,循环点击切换箭头,直到匹配成功:
import java.time.YearMonth; import java.time.format.DateTimeFormatter; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; // 在HomePage类里继续添加元素和方法 @FindBy(xpath = "//div[contains(@class, 'datepicker-days')]/thead/tr[1]/th[2]") private WebElement calendarCurrentMonthYear; // 日历顶部显示的年月,比如"October 2024" @FindBy(xpath = "//div[contains(@class, 'datepicker-days')]/thead/tr[1]/th[1]") private WebElement prevMonthBtn; // 上月切换箭头 @FindBy(xpath = "//div[contains(@class, 'datepicker-days')]/thead/tr[1]/th[3]") private WebElement nextMonthBtn; // 下月切换箭头 // 通用的切换到目标年月的方法 private void navigateToTargetMonth(YearMonth targetYearMonth) { WebDriverWait wait = new WebDriverWait(driver, 10); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM yyyy"); while (true) { // 获取当前日历显示的年月并解析 String currentMonthYearText = calendarCurrentMonthYear.getText().trim(); YearMonth currentYearMonth = YearMonth.parse(currentMonthYearText, formatter); if (currentYearMonth.equals(targetYearMonth)) { break; // 匹配到目标年月,停止切换 } else if (currentYearMonth.isBefore(targetYearMonth)) { // 当前年月早于目标,点下月按钮 wait.until(ExpectedConditions.elementToBeClickable(nextMonthBtn)).click(); } else { // 当前年月晚于目标,点上月按钮 wait.until(ExpectedConditions.elementToBeClickable(prevMonthBtn)).click(); } } }
3. 编写通用的「日期选择」函数
切换到目标月份后,就可以定位并点击目标日期了。注意要判断日期是否可点击(避免选择过期日期):
import java.time.LocalDate; import org.openqa.selenium.By; // 在HomePage类里添加日期选择方法 private void selectTargetDate(LocalDate targetDate) { WebDriverWait wait = new WebDriverWait(driver, 10); // 定位目标日期的元素:日期单元格的text等于目标日期的天数字符串 WebElement targetDateElement = wait.until(ExpectedConditions.elementToBeClickable( By.xpath(String.format("//td[contains(@class, 'day') and text()='%d']", targetDate.getDayOfMonth())) )); // 确保日期不是灰化的不可选状态 if (!targetDateElement.getAttribute("class").contains("disabled")) { targetDateElement.click(); } else { throw new IllegalArgumentException("目标日期不可选,请检查日期是否合法"); } }
4. 组合成完整的日期选择方法
把上面的方法整合,对外提供一个简单的调用入口,传入LocalDate类型的目标日期即可:
// 在HomePage类里添加最终的公共方法 public void selectCheckinDate(LocalDate targetDate) { openCheckinCalendar(); navigateToTargetMonth(YearMonth.from(targetDate)); selectTargetDate(targetDate); }
测试类中调用示例
在你的测试代码里,只需要实例化HomePage,传入目标日期即可完成操作:
import java.time.LocalDate; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; public class HotelBookingTest { public static void main(String[] args) { WebDriver driver = new ChromeDriver(); driver.get("https://phptravels.net/"); HomePage homePage = new HomePage(driver); // 选择3天后的日期作为入住日期 LocalDate checkinDate = LocalDate.now().plusDays(3); homePage.selectCheckinDate(checkinDate); // 后续的退房日期选择可以复用同样的逻辑,只需要修改定位元素即可 } }
通用函数的扩展思路
这套逻辑可以很容易改造成通用的日历处理工具类:
- 把年月切换、日期选择的核心逻辑抽离成独立的
CalendarHandler类,传入不同的日历元素定位器(比如不同网站的年月元素、切换按钮的定位) - 支持传入不同的日期格式解析器,适配不同网站的年月显示格式
- 添加更多边界处理,比如处理闰年、当月最后一天等特殊情况
备注:内容来源于stack exchange,提问作者Bhavesh Deshmukh
相关产品推荐
相关产品推荐

