【问题标题】:What is the fastest way to see if an element contains a child element?查看元素是否包含子元素的最快方法是什么?
【发布时间】:2016-11-03 18:42:37
【问题描述】:

我有一个 IWebElement(一个 div),其中一半时间包含一个子元素。我想看看它是否包含子元素,如果是,我想捕获它。我做这样的事情:

IWebElement firstChild;
try 
{
    firstChild = divElement.FindElement(By.XPath("*"));
}
catch (NoSuchElementException e)
{
    // catch and handling...
}

但这在三个部分上很慢:
1. By.XPath("*")的使用
2. FindElement 在元素不存在的情况下变慢
3. try/catch 是一个较慢的机制,我宁愿想要一个返回的布尔值对一个项目的存在

如何加快这种检测子元素的方式?

编辑:
澄清一下:我正在执行的测试在带有 div 的网格上运行,典型的网格是 4 x 16,所以 64 个字段。我想将此网格转换为 DataTable 以将其与预期结果进行比较。将这些字段捕获到数据表总共需要 22 秒。它的表现不是很糟糕,但我想把那些宝贵的时间剃掉。

更新:
我设法通过使用HTML Agility Pack 捕获网格来做到这一点。不幸的是(总有一些东西),input 元素的值无法被捕获,因为它们是动态设置的。作为一种解决方案,我会让 HAP 返回输入元素的 Id,并使用 FindElement(By.Id(inputId)) 捕获值,这与其他 Selenium 选择方法相比速度非常快。

长话短说:我设法将捕获时间从大约 22 秒减少到不到 3 秒,性能提高了 600% 以上。

【问题讨论】:

标签: c# selenium selenium-webdriver


【解决方案1】:

您需要在 XPath 前面添加 . 以在后代中搜索:

var elems = divElement.FindElements(By.XPath("(.//*)[1]"));
IWebElement firstChild = elems.Count > 0 ? elems[0] : null;

作为替代方案,您可以使用 CSS 选择器。它可能会稍微快一点:

var elems = divElement.FindElements(By.CssSelector("*"));
IWebElement firstChild = elems.Count > 0 ? elems[0] : null;

【讨论】:

  • 我喜欢总是返回一个元素的严格 XPath。不幸的是,它没有帮助。也许我只是达到了极限。
  • 大部分成本是在向驱动程序发送命令。所以如果你想显着减少时间,你应该避免在循环中调用FindElements。相反,尝试使用单个FindElements 定位所有divElement 中的所有孩子。另一种方法是使用 JavaScript 调用来计算孩子数:((IJavaScriptExecutor)driver).ExecuteScript("return [].map.call(arguments, function(e){return e.children.length})", elements)
  • 这实际上是一个好主意:使用FindElements 对所有元素进行一次广泛查找,然后遍历元素同时忽略我不需要的元素。去测试一下。
  • 添加了关于解决缓慢的不同方法的更新。我想出了这个解决方案,这要归功于您说重复调用 FindElements 由于重复调用驱动程序很慢。
【解决方案2】:

像这样在ISearchContext 上创建一个扩展方法:

public static IWebElement FindElementInstant(this ISearchContext context, By by)
{
    Driver.Instance.Manage().Timeouts().ImplicitlyWait(TimeSpan.Zero);

    var matchingElement = context.FindElements(by).FirstOrDefault();

    Driver.Instance.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(/* whatever your implicit wait is normally */));

    return matchingElement;
}

像这样使用:

var firstChild = divElement.FindElementInstant(By.XPath("*"));

如果您不需要访问匹配的元素,您可以从FindElementInstant 中返回bool

通过暂时禁用您的隐式等待,如果不存在匹配元素,FindElements 将立即返回一个空集合,并且通过在try-catch 中使用FindElements 而不是FindElement,您可以避免来自异常的额外时间。

【讨论】:

  • 我的 ImplicitWait 已经设置为零。 FindElements().FirstOrDefault 结合 Florent B 给出的严格 xpath 是一个很好的技巧。不幸的是,它并没有使查找速度更快。
  • 也许我对你正在遍历的 DOM 没有很好的理解,但是 22 秒是很长的时间。你确定后台没有任何隐式/显式等待吗?或者你有一个事件处理程序?
猜你喜欢
  • 2021-11-03
  • 2013-07-31
  • 1970-01-01
  • 2010-09-28
  • 1970-01-01
  • 1970-01-01
  • 2023-02-10
  • 2011-04-14
  • 1970-01-01
相关资源
最近更新 更多