如何使用 selenium 自动化阴影 DOM 元素?
How to automate shadow DOM elements using selenium?
我正在使用 Java Selenium 项目进行网页自动化。该网页包含许多我无法使用 selenium findElement
方法与之交互的多级 shadow-root DOM 元素。
我尝试了以下解决方案:
- deep css(不适用于最新的 chrome 浏览器)
- JS 执行器。 (这真的很乏味,维护起来也很复杂)
注:
如果您知道我可以在 Selenium Java 框架中实现的上述解决方案以外的任何其他解决方案,请传递解决方案。提前致谢!
使用 Selenium v3.x[=73 演示 shadow DOM 的 自动化 =], ChromeDriver v2.46 和 Chrome v73.x 这里有一些方法打开 url chrome://downloads/
并使用 executeScript()
方法发送 字符序列 pdf 作为 在搜索框中搜索文本。
使用document.querySelector()
作为规范方法,您可以使用 document.querySelector()
方法,如下所示:
代码块:
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class shadow_DOM_search_download_querySelector {
public static void main(String[] args)
{
System.setProperty("webdriver.chrome.driver", "C:\Utility\BrowserDrivers\chromedriver.exe");
ChromeOptions options = new ChromeOptions();
options.addArguments("start-maximized");
options.addArguments("disable-infobars");
options.addArguments("--disable-extensions");
WebDriver driver = new ChromeDriver(options);
driver.get("chrome://downloads/");
JavascriptExecutor jse = (JavascriptExecutor) driver;
WebElement search_box = (WebElement) jse.executeScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar-search-field#search').shadowRoot.querySelector('div#searchTerm input#searchInput')");
String js = "arguments[0].setAttribute('value','pdf')";
((JavascriptExecutor) driver).executeScript(js, search_box);
}
}
相同的解决方案可以按如下方式重写:
代码块:
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class shadow_DOM {
static WebDriver driver;
public static void main(String[] args)
{
System.setProperty("webdriver.chrome.driver", "C:\Utility\BrowserDrivers\chromedriver.exe");
ChromeOptions options = new ChromeOptions();
options.addArguments("start-maximized");
//options.addArguments("disable-infobars");
options.addArguments("--disable-extensions");
driver = new ChromeDriver(options);
driver.get("chrome://downloads/");
WebElement root1 = driver.findElement(By.tagName("downloads-manager"));
WebElement shadow_root1 = expand_shadow_element(root1);
WebElement root2 = shadow_root1.findElement(By.cssSelector("downloads-toolbar#toolbar"));
WebElement shadow_root2 = expand_shadow_element(root2);
WebElement root3 = shadow_root2.findElement(By.cssSelector("cr-toolbar#toolbar"));
WebElement shadow_root3 = expand_shadow_element(root3);
WebElement root4 = shadow_root3.findElement(By.cssSelector("cr-toolbar-search-field#search"));
WebElement shadow_root4 = expand_shadow_element(root4);
WebElement search_term = shadow_root4.findElement(By.cssSelector("div#searchTerm input#searchInput"));
String js = "arguments[0].setAttribute('value','pdf')";
((JavascriptExecutor) driver).executeScript(js, search_term);
WebElement search_button = shadow_root4.findElement(By.cssSelector("paper-icon-button#icon"));
search_button.click();
System.out.println("Search Button Clicked");
}
public static WebElement expand_shadow_element(WebElement element)
{
WebElement shadow_root = (WebElement)((JavascriptExecutor)driver).executeScript("return arguments[0].shadowRoot", element);
return shadow_root;
}
}
控制台输出:
Search Button Clicked
- 浏览器快照:
结尾
根据 Determine the fate of experimental '>>>' combinator 中的讨论,>>>
组合器替代了 /deep/
用于穿透所有阴影的组合器 DOM 样式边界,在 Blink 中实现的标志已被弃用。
有一个非常好的插件可以与 selenium 项目一起使用 shadow-automation-selenium。它有助于编写更好、可读和可维护的代码。使用它,您可以访问多级阴影 DOM(最多 4 级)。这使用简单的 css selector 来识别元素。
WebElement findElement(String cssSelector)
:如果想要 DOM
中的单个元素,请使用此方法
List<WebElement> findElements(String cssSelector)
:如果你想从 DOM
中找到所有元素,请使用它
WebElement findElements(WebElement parent, String cssSelector)
:如果您想从父对象 DOM
中查找单个元素,请使用此选项
List<WebElement> findElements(WebElement parent, String cssSelector)
:如果你想从父对象 DOM
中查找所有元素,请使用它
WebElement getShadowElement(WebElement parent,String selector)
:如果您想从父 DOM
中查找单个元素,请使用此选项
List<WebElement> getAllShadowElement(WebElement parent,String selector)
:如果你想从父 DOM
中找到所有元素,请使用它
boolean isVisible(WebElement element)
:如果要查找元素的可见性,请使用此选项
boolean isChecked(WebElement element)
:如果要检查复选框是否 selected
,请使用此选项
boolean isDisabled(WebElement element)
:如果要检查元素是否被禁用,请使用此选项
String getAttribute(WebElement element,String attribute)
:如果你想获得像 aria-selected 这样的属性和元素的其他自定义属性,请使用它。
void selectCheckbox(String label)
: 将此用于 select 使用标签的复选框元素。
void selectCheckbox(WebElement parentElement, String label)
: 将此用于 select 使用标签的复选框元素。
void selectRadio(String label)
: 将此用于 select 使用标签的无线电元素。
void selectRadio(WebElement parentElement, String label)
:使用此标签 select 来自父 DOM 的单选元素。
void selectDropdown(String label)
:将此用于 select 使用标签的下拉列表项(如果 UI 上仅存在或加载一个下拉列表,则使用此选项)。
void selectDropdown(WebElement parentElement, String label)
:使用此标签 select 来自父级 DOM 的下拉列表项。
如何使用这个插件:
你将不得不依赖你的项目。
Maven
<dependency>
<groupId>io.github.sukgu</groupId>
<artifactId>automation</artifactId>
<version>0.0.4</version>
<dependency>
对于位于 shadow-root dom 元素下的 html 标签
<properties-page id="settingsPage">
<textarea id="textarea">
</properties-page>
您可以在您的框架中使用此代码来获取 textarea 元素对象。
import io.github.sukgu.*;
Shadow shadow = new Shadow(driver);
WebElement element = shadow.findElement("properties-page#settingsPage>textarea#textarea");
String text = element.getText();
使用 JSExecutor 找出阴影 DOM 元素的步骤 And CSS:
找出基本元素,即 Shadow 根元素的父元素。
获取该元素的影子根。
并在那个 shadow-root 网络元素上找到你的元素
示例:
<div id="example">
#shadow-root
<div id="root" part="root">
<div id="label" part="label">ShadowRootLabel</div>
</div>
</ptcs-label>
#影根元素的查找方法
public WebElement getShadowRootElement(WebElement element) {
WebElement ele = (WebElement) ((JavascriptExecutor)driver)
.executeScript("return arguments[0].shadowRoot", element);
return ele;
}
#Step1 例如,找到基本元素:
WebElement root1 = driver.findElement(By.id("example"));
#Step2
//Get shadow root element
WebElement shadowRoot1 = getShadowRootElement(root1);
#Step3 - 我们需要使用 CSS 选择器来查找影子根内的元素,xpath 在这里不起作用
//Here we will get Element inside Shadow Dom Element
WebElement shadowElement = shadowRoot3.findElement(By.cssSelector("div[id=label]"));
Selenium 4 现在有 WebElement.getShadowRoot()
。例如:
driver.findElement(By.id("parentId")).getShadowRoot().findElement(By.cssSelector( "label" )).findElement(By.tagName("input"))
与 #shadow-root
一样,下一跳的导航选择是有限的。例如。针对 Chrome By.cssSelector()
和 By.className()
有效,但 By.id()
和 By.tagName()
失败 org.openqa.selenium.InvalidArgumentException: invalid argument: invalid locator
我正在使用 Java Selenium 项目进行网页自动化。该网页包含许多我无法使用 selenium findElement
方法与之交互的多级 shadow-root DOM 元素。
我尝试了以下解决方案:
- deep css(不适用于最新的 chrome 浏览器)
- JS 执行器。 (这真的很乏味,维护起来也很复杂)
注:
如果您知道我可以在 Selenium Java 框架中实现的上述解决方案以外的任何其他解决方案,请传递解决方案。提前致谢!
使用 Selenium v3.x[=73 演示 shadow DOM 的 自动化 =], ChromeDriver v2.46 和 Chrome v73.x 这里有一些方法打开 url chrome://downloads/
并使用 executeScript()
方法发送 字符序列 pdf 作为 在搜索框中搜索文本。
使用document.querySelector()
作为规范方法,您可以使用 document.querySelector()
方法,如下所示:
代码块:
import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; public class shadow_DOM_search_download_querySelector { public static void main(String[] args) { System.setProperty("webdriver.chrome.driver", "C:\Utility\BrowserDrivers\chromedriver.exe"); ChromeOptions options = new ChromeOptions(); options.addArguments("start-maximized"); options.addArguments("disable-infobars"); options.addArguments("--disable-extensions"); WebDriver driver = new ChromeDriver(options); driver.get("chrome://downloads/"); JavascriptExecutor jse = (JavascriptExecutor) driver; WebElement search_box = (WebElement) jse.executeScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar-search-field#search').shadowRoot.querySelector('div#searchTerm input#searchInput')"); String js = "arguments[0].setAttribute('value','pdf')"; ((JavascriptExecutor) driver).executeScript(js, search_box); } }
相同的解决方案可以按如下方式重写:
代码块:
import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; public class shadow_DOM { static WebDriver driver; public static void main(String[] args) { System.setProperty("webdriver.chrome.driver", "C:\Utility\BrowserDrivers\chromedriver.exe"); ChromeOptions options = new ChromeOptions(); options.addArguments("start-maximized"); //options.addArguments("disable-infobars"); options.addArguments("--disable-extensions"); driver = new ChromeDriver(options); driver.get("chrome://downloads/"); WebElement root1 = driver.findElement(By.tagName("downloads-manager")); WebElement shadow_root1 = expand_shadow_element(root1); WebElement root2 = shadow_root1.findElement(By.cssSelector("downloads-toolbar#toolbar")); WebElement shadow_root2 = expand_shadow_element(root2); WebElement root3 = shadow_root2.findElement(By.cssSelector("cr-toolbar#toolbar")); WebElement shadow_root3 = expand_shadow_element(root3); WebElement root4 = shadow_root3.findElement(By.cssSelector("cr-toolbar-search-field#search")); WebElement shadow_root4 = expand_shadow_element(root4); WebElement search_term = shadow_root4.findElement(By.cssSelector("div#searchTerm input#searchInput")); String js = "arguments[0].setAttribute('value','pdf')"; ((JavascriptExecutor) driver).executeScript(js, search_term); WebElement search_button = shadow_root4.findElement(By.cssSelector("paper-icon-button#icon")); search_button.click(); System.out.println("Search Button Clicked"); } public static WebElement expand_shadow_element(WebElement element) { WebElement shadow_root = (WebElement)((JavascriptExecutor)driver).executeScript("return arguments[0].shadowRoot", element); return shadow_root; } }
控制台输出:
Search Button Clicked
- 浏览器快照:
结尾
根据 Determine the fate of experimental '>>>' combinator 中的讨论,>>>
组合器替代了 /deep/
用于穿透所有阴影的组合器 DOM 样式边界,在 Blink 中实现的标志已被弃用。
有一个非常好的插件可以与 selenium 项目一起使用 shadow-automation-selenium。它有助于编写更好、可读和可维护的代码。使用它,您可以访问多级阴影 DOM(最多 4 级)。这使用简单的 css selector 来识别元素。
WebElement findElement(String cssSelector)
:如果想要 DOM
List<WebElement> findElements(String cssSelector)
:如果你想从 DOM
WebElement findElements(WebElement parent, String cssSelector)
:如果您想从父对象 DOM
List<WebElement> findElements(WebElement parent, String cssSelector)
:如果你想从父对象 DOM
WebElement getShadowElement(WebElement parent,String selector)
:如果您想从父 DOM
List<WebElement> getAllShadowElement(WebElement parent,String selector)
:如果你想从父 DOM
boolean isVisible(WebElement element)
:如果要查找元素的可见性,请使用此选项
boolean isChecked(WebElement element)
:如果要检查复选框是否 selected
boolean isDisabled(WebElement element)
:如果要检查元素是否被禁用,请使用此选项
String getAttribute(WebElement element,String attribute)
:如果你想获得像 aria-selected 这样的属性和元素的其他自定义属性,请使用它。
void selectCheckbox(String label)
: 将此用于 select 使用标签的复选框元素。
void selectCheckbox(WebElement parentElement, String label)
: 将此用于 select 使用标签的复选框元素。
void selectRadio(String label)
: 将此用于 select 使用标签的无线电元素。
void selectRadio(WebElement parentElement, String label)
:使用此标签 select 来自父 DOM 的单选元素。
void selectDropdown(String label)
:将此用于 select 使用标签的下拉列表项(如果 UI 上仅存在或加载一个下拉列表,则使用此选项)。
void selectDropdown(WebElement parentElement, String label)
:使用此标签 select 来自父级 DOM 的下拉列表项。
如何使用这个插件: 你将不得不依赖你的项目。
Maven
<dependency>
<groupId>io.github.sukgu</groupId>
<artifactId>automation</artifactId>
<version>0.0.4</version>
<dependency>
对于位于 shadow-root dom 元素下的 html 标签
<properties-page id="settingsPage">
<textarea id="textarea">
</properties-page>
您可以在您的框架中使用此代码来获取 textarea 元素对象。
import io.github.sukgu.*;
Shadow shadow = new Shadow(driver);
WebElement element = shadow.findElement("properties-page#settingsPage>textarea#textarea");
String text = element.getText();
使用 JSExecutor 找出阴影 DOM 元素的步骤 And CSS:
找出基本元素,即 Shadow 根元素的父元素。
获取该元素的影子根。
并在那个 shadow-root 网络元素上找到你的元素
示例:
<div id="example">
#shadow-root
<div id="root" part="root">
<div id="label" part="label">ShadowRootLabel</div>
</div>
</ptcs-label>
#影根元素的查找方法
public WebElement getShadowRootElement(WebElement element) {
WebElement ele = (WebElement) ((JavascriptExecutor)driver)
.executeScript("return arguments[0].shadowRoot", element);
return ele;
}
#Step1 例如,找到基本元素:
WebElement root1 = driver.findElement(By.id("example"));
#Step2
//Get shadow root element
WebElement shadowRoot1 = getShadowRootElement(root1);
#Step3 - 我们需要使用 CSS 选择器来查找影子根内的元素,xpath 在这里不起作用
//Here we will get Element inside Shadow Dom Element
WebElement shadowElement = shadowRoot3.findElement(By.cssSelector("div[id=label]"));
Selenium 4 现在有 WebElement.getShadowRoot()
。例如:
driver.findElement(By.id("parentId")).getShadowRoot().findElement(By.cssSelector( "label" )).findElement(By.tagName("input"))
与 #shadow-root
一样,下一跳的导航选择是有限的。例如。针对 Chrome By.cssSelector()
和 By.className()
有效,但 By.id()
和 By.tagName()
失败 org.openqa.selenium.InvalidArgumentException: invalid argument: invalid locator