selenium 使用三(dom元素定位、异常处理)
一、selenium 定位网页中的元素
查看WebDriver类,可以看到:
java.util.List<org.openqa.selenium.WebElement> findElements(org.openqa.selenium.By by);
org.openqa.selenium.WebElement findElement(org.openqa.selenium.By by);
也就是说,定位元素主要通过findElement的传参By by,再进入By类可以看到:
主要有以下几种方法:
public static org.openqa.selenium.By id(java.lang.String id) { /* compiled code */ }
public static org.openqa.selenium.By linkText(java.lang.String linkText) { /* compiled code */ }
public static org.openqa.selenium.By partialLinkText(java.lang.String partialLinkText) { /* compiled code */ }
public static org.openqa.selenium.By name(java.lang.String name) { /* compiled code */ }
public static org.openqa.selenium.By tagName(java.lang.String tagName) { /* compiled code */ }
public static org.openqa.selenium.By xpath(java.lang.String xpathExpression) { /* compiled code */ }
public static org.openqa.selenium.By className(java.lang.String className) { /* compiled code */ }
public static org.openqa.selenium.By cssSelector(java.lang.String cssSelector) { /* compiled code */ }
public org.openqa.selenium.WebElement findElement(org.openqa.selenium.SearchContext context) { /* compiled code */ }
public abstract java.util.List<org.openqa.selenium.WebElement> findElements(org.openqa.selenium.SearchContext searchContext);
使用id\name\className定位,查找元素id属性为kw
WebElement element = driver.findElement(By.id("kw"));
同理,name(),className(),也是依次查找name\class值来定位元素
linkText\partialLinkText定位,查找超链接的文本
html示例
<a class="title-content c-link c-font-medium c-line-clamp1" href="https://www.baidu.com/" target="_blank"><span class="title-content-index c-index-single c-index-single-hot1">1</span><span class="title-content-title">31省区市新增确诊25例 本土15例</span><span class="title-content-mark c-text c-gap-left-small c-text-new">新</span></a>
driver.findElement(By.linkText("https://www.baidu.com/")); // 精确匹配
driver.findElement(By.partialLinkText("baidu")); // 模糊匹配
cssSelector定位
查找某元素使用了指定的css样式
driver.findElement(By.cssSelector(".c-index-single-hot1"));
xpath定位(推荐使用)
xpath是个人比较推荐的用法,可以用来实现几乎所有元素查找的功能
By.xpath("//div[@id='kw']") // id定位
By.xpath("//span[@class='title-content-title']") //class精确定位
By.xpath("//span[contains(@class,'c-index-single-hot1')]") // class 模糊定位
By.xpath("//a[contains(@class,'c-line-clamp1') and @target='_blank']") // 使用and实现更精确的匹配(如图)
By.xpath("//li[@class='hotsearch-item odd']/a[1]") // 通过定位父级来查找子级
By.xpaht("//li[@class='bui-pagination-num'][last()]") // 获取最后一个复合条件的li元素
By.xpath("//a[contains(@class,'c-line-clamp1')/..") // 通过定位a链接,查找到父级li
By.xpath("//a[contains(@class,'c-line-clamp1') and @target='_blank']/../span[2]") // 通过定位a链接,查找到父级li,再查找同级的span
二、异常处理
1.网页更新导致或代码定位没写对时,会导致元素查找失败,因此查找元素需要抛异常(不推荐)
try {
driver.findElement(By.partialLinkText("baidu"));
}catch (Exception exception) {
exception.printStackTrace();
}
2.通过findElements来代替findElement
List<WebElement> elements = driver.findElements(By by);
if(elements.size() > 0) {
// 操作
}else {
System.out.println("查找不到当前元素!");
}
三、查找元素等待时间
一般情况下,查找的元素都是比较重要的元素,在查找之前,要先判断网页的dom树是否渲染加载完成,这是findElement存在多个等待时间:
driver.get("https://www.baidu.com"); # 加载流程
Thread.sleep(3000); # 硬性等待3秒,有些页面通过js渲染dom树
driver.findElement(By.partialLinkText("baidu")); # 查找元素
上面流程,实际操作会遇到几种情况:
1.driver.get()操作设置非阻塞模式
网页加载过于缓慢(图片、js等过多), 或者网络卡顿,此时driver.get()的时间就会过长。可以通过设置PageLoadStrategy属性来优化, 这样get()操作不会阻塞进程,get瞬间,同时开始执行后续操作。
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setPageLoadStrategy(PageLoadStrategy.NONE);
ChromeDriver driver = new ChromeDriver(chromeOptions);
2.findElement和findElments等隐性等待时间
但是,这样也存在一个问题,就是网页加载完成与否,无法判断,因此需要一个时间等待来响应,常见的有Thread.sleep,之后执行findElement。findElement等待操作的过程,也是一个n秒有效时间内查找等待网页刷新,再查找的过程。这里的n秒有效时间,也就是隐性等待时间,可以设置(一般不推荐太长,避免程序在查找不到元素的时候等待时间过长):
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
3.通过WebDriverWait来实现等待时间控制:
seconds,就是指定的等待时间
public static boolean isVisibility(WebDriver driver,int seconds,String... xpathExpressions) {
for (String xpathExpression: xpathExpressions) {
try {
WebDriverWait wait = new WebDriverWait(driver, seconds);
// 判断元素是否出现
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(xpathExpression)));
// 判断是否至少有 1 个元素存在于 dom 树中。举个例子,如果页面上有 n 个元素的 class 都是’column-md-3’,那么只要有 1 个元素存在,这个方法就返回 True。
//wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(xpathExpression)));
}catch (Exception exception) {
return false;
}
}
return true;
}
// 调用判断
if(DriverUtil.isVisibility(driver,3000, "//div[@id='kw']")) {
WebElement element = dirver.findElement(By.xpath("//div[@id='kw']"));
}else {
System.out.println("查找不到当前元素!");
}
四、一些异常处理:
最近开发遇到这么一段代码执行的时候报错:
WebElement item = driver.findElement(By.xpath("//div[@id='ctrlselectFundTypeSelectitem0']"));
item.click();
报错信息:
element not interactable
(Session info: headless chrome=87.0.4280.66)
Build info: version: 'unknown', revision: 'unknown', time: 'unknown'
System info: host: 'localhost.localdomain', ip: '127.0.0.1', os.name: 'Linux', os.arch: 'amd64', os.version: '3.10.0-1062.el7.x86_64', java.version: '14.0.1'
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 87.0.4280.66, chrome: {chromedriverVersion: 87.0.4280.87 (b7fed45dbe27a..., userDataDir: /tmp/.com.google.Chrome.KKkill}, goog:chromeOptions: {debuggerAddress: localhost:38680}, javascriptEnabled: true, networkConnectionEnabled: false, pageLoadStrategy: eager, platform: LINUX, platformName: LINUX,proxy: Proxy(), setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: dismiss and notify, webauthn:virtualAuthenticators: true}
Session ID: 096d1e9b1a7c4d994e193616c19ab35c
这个怎么回事呢,其实就是元素虽然查找到了,但是在页面中的位置非可见的(或者说z-index浮层被别的元素覆盖),这种情况需要检查确定元素是否可见:
item.isDisplayed();