如何在 Selenium WebDriver 中使用 xPath 来抓取 SVG 元素?

Posted

技术标签:

【中文标题】如何在 Selenium WebDriver 中使用 xPath 来抓取 SVG 元素?【英文标题】:How to use xPath in Selenium WebDriver to grab SVG elements? 【发布时间】:2015-10-09 19:41:22 【问题描述】:

我正在使用 Selenium WebDriver(Java 版本)测试基于 OpenLayers 的 API。

我想测试一个使用OpenLayers.Control.ModifyFeature 的功能。我想单击绘制的特征 (SVG),然后拖动并检查它们是否存在、可见或隐藏。

我画了一个多边形,我已经选择了它。见下图:

这些 SVG 元素的 html 在这里:

<svg id="OpenLayers_Layer_Vector_161_svgRoot"   viewBox="0 0 1235 495" style="display: block;">
    <g id="OpenLayers_Layer_Vector_161_root" transform="" style="visibility: visible;">
        <g id="OpenLayers_Layer_Vector_161_vroot">
            <path id="OpenLayers_Geometry_Polygon_200" d=" M 393.0000000000964,213.9999999999891 486.0000000003338,275.9999999997126 384.00000000036925,284.9999999994434 393.0000000000964,213.9999999999891 z" fill-rule="evenodd" fill="blue" fill-opacity="0.4" stroke="blue" stroke-opacity="1" stroke- stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="pointer" />
            <circle id="OpenLayers_Geometry_Point_619" cx="439.50000000021464" cy="244.99999999985084" r="6" fill="#009900" fill-opacity="0.5" stroke="#ee9900" stroke-opacity="1" stroke- stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
            <circle id="OpenLayers_Geometry_Point_621" cx="435.00000000035106" cy="280.49999999958163" r="6" fill="#009900" fill-opacity="0.5" stroke="#ee9900" stroke-opacity="1" stroke- stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
            <circle id="OpenLayers_Geometry_Point_623" cx="388.50000000023283" cy="249.4999999997126" r="6" fill="#009900" fill-opacity="0.5" stroke="#ee9900" stroke-opacity="1" stroke- stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
            <circle id="OpenLayers_Geometry_Point_202" cx="393.0000000000964" cy="213.9999999999891" r="6" fill="#990000" fill-opacity="1" stroke="#ee9900" stroke-opacity="1" stroke- stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
            <circle id="OpenLayers_Geometry_Point_203" cx="486.0000000003338" cy="275.9999999997126" r="6" fill="#990000" fill-opacity="1" stroke="#ee9900" stroke-opacity="1" stroke- stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
            <circle id="OpenLayers_Geometry_Point_204" cx="384.00000000036925" cy="284.9999999994434" r="6" fill="#990000" fill-opacity="1" stroke="#ee9900" stroke-opacity="1" stroke- stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
        </g>
        <g id="OpenLayers_Layer_Vector_161_troot" />
    </g>
</svg>

假设我想选择红点。

我试过了:

String xpath = "//circle[contains(@id, 'OpenLayers_Geometry_Point') AND fill = '#990000']";
List<WebElement> vertices = driver.findElements(By.xpath(xpath));

但它总是返回一个空列表[]

我在这里做错了什么?有人可以帮帮我吗?

非常感谢。

编辑 1 - 函数:verticesAreVisible

在单击操作之前,我想获取元素并检查它们是否可见。我正在使用这个功能。

public static boolean verticesAreVisible(WebDriver driver, String xpath) 
    List<WebElement> list = driver.findElements(By.xpath(xpath));
    if (list.isEmpty()) 
        return false;
    
    boolean visible = true;
    for (int i = 0; i < list.size(); i++) 
        visible = visible && list.get(i).isDisplayed();
    
    return !verticesAreNotVisible(driver) && visible;

编辑 2 - 正确的 xPath

// This solution from Razib is valid if the SVG is on the root node
String xpath = "/*[name()='svg']/*[name()='circle']";
// I changed it so that any descendant is valid "//"
String xpath = "//*[name()='svg']//*[name()='circle']";
// Since I wanted only the red vertices, I added this
String xpath = "//*[name()='svg']//*[name()='circle' and @fill='#990000']";

【问题讨论】:

您好,我发现这个 xpath 更易于使用,不需要任何操作。 //*[local-name()='svg']//*[local-name()='g' and and @fill='#990000''] 【参考方案1】:

要定位红点,即具有属性 fill="#990000"id 属性包含 OpenLayers_Geometry_Point 的元素,您可以使用以下任一 Locator Strategies:

使用xpath:

//*[name()='svg']/*[name()='g']/*[name()='g']//*[name()='circle' and contains(@fill, '990000')][starts-with(@id, 'OpenLayers_Geometry_Point')]

使用css-selectors:

svg > g > g circle[fill$='990000'][id^='OpenLayers_Geometry_Point']

理想情况下,您需要为visibilityOfAllElementsLocatedBy() 诱导WebDriverWait,并且可以使用以下任一定位器策略

使用cssSelector

List<WebElement> vertices = new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("svg > g > g circle[fill$='990000'][id^='OpenLayers_Geometry_Point']")));

使用xpath

List<WebElement> vertices = new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath("//*[name()='svg']/*[name()='g']/*[name()='g']//*[name()='circle' and contains(@fill, '990000')][starts-with(@id, 'OpenLayers_Geometry_Point')]")));

参考

您可以在以下位置找到一些相关的详细讨论:

How to click on SVG elements using XPath and Selenium WebDriver through Java Clicking on svg using selenium python Unable to locate SVG elements through xpath on Kendo UI chart Creating XPATH for svg tag How to access to 'rect' type element through Selenium-Python

【讨论】:

【参考方案2】:

尝试了各种技术潜入旅馆,但最后我找到了一种简单的方法来处理这种情况。 当您想要的 svg 没有名称并且适当的 xpath 动态更改时,我使用了“cssSelector”

WebElement DesiredSvg = driver.findElement(By.cssSelector("#scrollable-auto-tabpanel-0 > div > div > form > div:nth-child(1) > div:nth-child(1) > div > div > div.jss10127 > div > div.jss11602 > svg:nth-child(3)"));

DesiredSvg.click();

【讨论】:

【参考方案3】:

在我们实现 SVG 的屏幕之一中,我们也遇到了类似的问题,我使用动作类解决了。

Action Class Package :
java.lang.Object
org.openqa.selenium.interactions.Actions

示例代码:

WebElement svgObject= driver.findElement(By.xpath(XPATH));
Actions actionBuilderObj = new Actions(driver);
actionBuilderObj .click(svgObject).build().perform();

【讨论】:

【参考方案4】:

要获得可以使用的可见元素:

wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath("bla bla")));

【讨论】:

【参考方案5】:

您可能需要在Xpath 中使用带有name 属性的操作。 在你的 XPath 中使用它 -

"/*[name()='svg']/*[name()='SVG OBJECT']"  

然后试试下面的代码sn -p -

WebElement svgObj = driver.findElement(By.xpath(XPATH));
Actions actionBuilder = new Actions(driver);
actionBuilder.click(svgObj).build().perform();

【讨论】:

嗨@Razib。我花了一些时间才让这个工作,但多亏了你的提示,它现在可以工作了! :) 非常感谢!!!有关解决方案的更多详细信息,请参阅我的帖子中的编辑。【参考方案6】:

尝试用@fill 代替fillOpenLayers_Geometry_Point 代替OpenLayers.Geometry.Point

【讨论】:

嗨@peetya。感谢“OpenLayers_Geometry_Point”提示。我完全忘记用下划线替换点。无论如何,“@fill”提示不起作用。如果我使用这个 xPath '//*[contains(@id, 'OpenLayers_Geometry_Point')]',我会得到 6 分。但我只想要 3 红色。

以上是关于如何在 Selenium WebDriver 中使用 xPath 来抓取 SVG 元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 在 Selenium WebDriver (Selenium 2) 中最大化浏览器窗口?

如何使用selenium webdriver来判断一个网页加载完毕

如何在 ruby​​ 中使用 Selenium WebDriver (selenium 2.0) 客户端设置选项

如何在python selenium chrome webdriver中设置标头

如何在 Java 中使用 Selenium WebDriver (Selenium 2) 输入文本框?

如何在 selenium-webdriver 中为 phantomjs 驱动程序设置一个用户代理?