cssSelector和Xpath有什么区别,并且在跨浏览器测试的性能方面哪个更好?


87

我正在多语言Web应用程序上使用Selenium WebDriver 2.25.0,主要测试页面内容(用于阿拉伯语,英语,俄语等不同语言)。

对于我的应用程序,根据性能最好,并确保它应支持所有浏览器(即IE 7、8、9,FF,Chrome等)。

预先感谢您的宝贵建议。

Answers:


107

CSS选择器的性能远胜过Xpath,并且在Selenium社区中有很好的记录。这是一些原因,

  • Xpath引擎在每个浏览器中都不同,因此使其不一致
  • IE没有本机的xpath引擎,因此硒会注入自己的xpath引擎以实现其API的兼容性。因此,我们失去了使用WebDriver固有的本机浏览器功能的优势。
  • Xpath趋于复杂,因此在我看来很难阅读

但是,在某些情况下,您需要使用xpath,例如,搜索父元素或通过其文本搜索元素(我不建议稍后介绍)。

您可以在此处阅读Simon的博客。他还建议在Xpath上使用CSS。

如果要测试内容,请不要使用依赖于元素内容的选择器。这将是每个地区维护的噩梦。尝试与开发人员交谈,并使用他们用来外部化应用程序中文本的技术,例如词典或资源包等。这是我的博客,详细解释了它。

编辑1

感谢@parishodak,这里的链接提供了证明CSS性能更好的数字。


7
CSS选择器不允许文本。CSS中不推荐使用“ contains”。就像我在上面说的那样,选择器与内容无关。内容可以驻留在外部。您可以与开发人员交谈。他们必须将文本外化。在大多数情况下,每个区域都有字典。因此,字典中的键是相同的,但其值会根据语言环境而变化。您可以使用这些文件来验证内容。请注意,您需要使用JDK的nativ2ascii工具将本机字符转换为ASCII字符。我必须为此写博客。我确实使用这种技术测试了许多语言环境。
nilesh

1
我在答案中添加了我的博客链接。抱歉,花了一段时间。希望对您有所帮助。
nilesh 2013年

8
@Nilesh:我不同意你的回答。1.)CSS引擎在每个浏览器中也有所不同。这不是一个论点。3.)凭借一些经验,XPath非常易于理解,并且比CSS提供更多的功能。如果您搜索一个非常嵌套的元素,它们都是很复杂的:XPath和CSS。以我的经验,对这个问题的任何一般性回答都是错误的。决定CSS / XPATH必须单独进行。最初的问题是关于性能的。您的答案主要包括假设和个人意见。一个真正的证明将是测量性能并将结果发布在这里。
Elmue

2
一篇很好的文章与您的第一句话矛盾:“ CSS选择器的性能远胜于Xpath”。并不是那么简单,甚至可能相反。并且:“ IE没有本机的xpath引擎,因此硒会注入自己的xpath引擎以实现其API的兼容性。” 硒在这里存在设计错误。当然,最好用C ++而不是Java脚本来实现XPath引擎。但是IE已死,现在是Edge。在所有性能问题的背后,一定不要忘记CSS缺少非常重要的功能,例如搜索元素的文本。
Elmue 2015年

2
elementalselenium.com/tips/32-xpath-vs-css提供的基准表明,css3不再明显更快。
mc0e '16

46

我将对SO硒标签持有不受欢迎的观点,即从长远来看XPath比CSS更可取

这篇长篇文章分为两部分-首先,我将提供一张餐巾纸证明,两者之间的性能差异为0.1-0.3毫秒 (是的;这是100微秒,然后我将分享我的看法,为什么XPath功能更强大。


性能差异

让我们首先解决“房间里的大象”问题– xpath比CSS慢。

使用当前的cpu功能(阅读:自2013年以来生产的任何x86),即使是在浏览器堆栈/ saucelabs / aws VM上,以及浏览器的开发(阅读:过去5年中所有流行的浏览器,也并非如此。浏览器的引擎已经开发出来,xpath的支持是统一的,IE是无用的(对我们大多数人来说是希望的)。另一个地方的这种比较方法在各处都被引用,但这是非常具体的上下文-有多少个正在运行-或在乎-针对IE8的自动化?

如果存在差异,则相差不到一毫秒

但是,大多数高级框架无论如何都要在原始硒调用上增加至少1ms的开销(包装器,处理程序,状态存储等);我个人选择的武器– RobotFramework –至少增加了2毫秒,为此我很乐意为之牺牲。从AWS us-east-1到BrowserStack的集线器的网络往返通常为11毫秒

因此,在远程浏览器中,如果xpath和css之间存在差异,那么其他一切(数量级)都会使它黯然失色。


测量

公开比较不多(我只看过引用的比较,所以–这是一个粗略的单例,虚拟和简单的比较。
它将通过两种策略将元素定位X次,并比较该平均时间。

目标– BrowserStack的登录页面及其“注册”按钮;撰写本文的html屏幕截图:

在此处输入图片说明

这是测试代码(python):

from selenium import webdriver
import timeit


if __name__ == '__main__':

    xpath_locator = '//div[@class="button-section col-xs-12 row"]'
    css_locator = 'div.button-section.col-xs-12.row'

    repetitions = 1000

    driver = webdriver.Chrome()
    driver.get('https://www.browserstack.com/')

    css_time = timeit.timeit("driver.find_element_by_css_selector(css_locator)", 
                             number=repetitions, globals=globals())
    xpath_time = timeit.timeit('driver.find_element_by_xpath(xpath_locator)', 
                             number=repetitions, globals=globals())

    driver.quit()

    print("css total time {} repeats: {:.2f}s, per find: {:.2f}ms".
          format(repetitions, css_time, (css_time/repetitions)*1000))
    print("xpath total time for {} repeats: {:.2f}s, per find: {:.2f}ms".
          format(repetitions, xpath_time, (xpath_time/repetitions)*1000))

对于不熟悉Python的用户-首先打开页面并找到元素-首先使用CSS定位器,然后使用xpath;查找操作重复1,000次。输出是1000次重复的总时间(以秒为单位)和一次查找的平均时间(以毫秒为单位)。

定位器是:

  • 对于xpath –“在DOM中某个位置具有该确切类值的div元素”;
  • css类似于–“此类的div元素,在DOM中的某个位置”。

故意选择不要过度调音;同样,类选择器被css引用为“仅次于id的第二快”。

环境-Chrome v66.0.3359.139,chromedriver v2.38,cpu:ULV Core M-5Y10通常以1.5GHz运行(是的,是“文字处理”程序,甚至不是普通的i7野兽)

这是输出:

css total time 1000 repeats: 8.84s, per find: 8.84ms

xpath total time for 1000 repeats: 8.52s, per find: 8.52ms

显然,每次查找的时机都非常接近。差异为0.32 毫秒。不要跳过“ xpath更快” –有时是,有时是css。


让我们尝试另一套定位器,稍微复杂一点–一个具有子字符串的属性(至少对我来说是通用的方法,当元素的一部分具有功能含义时,它会追随元素的类)

xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'

这两个定位符在语义上再次相同–“在其类属性中找到具有此子字符串的div元素”。
结果如下:

css total time 1000 repeats: 8.60s, per find: 8.60ms

xpath total time for 1000 repeats: 8.75s, per find: 8.75ms

差异0.15毫秒


作为练习-在注释/其他答案中的链接博客中进行的测试相同-测试页面是公开的,测试代码也是如此。

他们在代码中做了几件事-单击列对其进行排序,然后获取值,并检查UI排序是否正确。
我要剪掉-毕竟要找到定位器-这是根测试,对吗?

与上面相同的代码,但有以下更改:

  • 现在的网址是http://the-internet.herokuapp.com/tables;有2个测试。

  • 第一个的定位器-“按ID和类查找元素”是:

css_locator = '#table2 tbody .dues'
xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"

结果如下:

css total time 1000 repeats: 8.24s, per find: 8.24ms

xpath total time for 1000 repeats: 8.45s, per find: 8.45ms

差异为0.2毫秒。

“通过遍历查找元素”:

css_locator = '#table1 tbody tr td:nth-of-type(4)'
xpath_locator = "//table[@id='table1']//tr/td[4]"

结果:

css total time 1000 repeats: 9.29s, per find: 9.29ms

xpath total time for 1000 repeats: 8.79s, per find: 8.79ms

这次是0.5毫秒(相反,xpath在这里“更快”)。

因此5年后(更好的浏览器引擎),并且只关注定位器的性能(没有在UI中进行排序等操作),相同的测试平台-CSS和XPath实际上没有区别。


那么,在xpath和CSS中,从性能中选择哪两个呢?答案很简单–选择通过id查找

长话短说,如果一个元素的id是唯一的(按照规范的规定),则它的值在浏览器的DOM内部表示中起着重要的作用,因此通常是最快的。

但是,唯一且恒定(例如,不是自动生成的) ID并不总是可用,这使我们想到“如果有CSS,为什么要使用XPath?”


XPath的优势

由于性能出乎意料,为什么我认为xpath更好?简单-多功能性和力量。

Xpath是为处理XML文档而开发的一种语言。这样,它允许使用比CSS更强大的构造。
例如,在树中的每个方向上导航–找到一个元素,然后转到其祖父母并搜索具有某些属性的子元素。
它允许嵌入式布尔条件- cond1 and not(cond2 or not(cond3 and cond4)); 嵌入式选择器–“使用这些属性的孩子找到一个div,然后根据它进行导航”。
XPath允许基于节点的值(它的文本)进行搜索-但是对此做法不满意,它确实派上用场,尤其是在结构不良的文档中(没有明确的属性可以踩,例如动态id和class-通过文本确定元素内容)

进入CSS肯定更容易-只需几分钟即可开始编写选择器;但是经过几天的使用,xpath的强大功能和潜力很快克服了CSS。
纯粹是主观的–复杂的CSS比复杂的xpath表达式难读。

Outro;)

最后,还是很主观的-选择哪个?

海事组织,没有正确或错误的选择-它们是解决同一问题的不同方法,应选择更适合该工作的方法。

作为XPath的“狂热者”,我并不害羞在项目中同时使用这两者-哎呀,有时候抛出CSS会更快,如果我知道它会做得很好的话。


登录页面有多少个节点?登录页面通常非常简单,因此您可能会发现差异很小。
pagep '18年

其他性能测试显示,不同浏览器之间的差异更大。
pagep

1
对于第一个问题-答案中显示了DOM的屏幕截图,并且该页面在线且公开。对于您的第一个和第二个,如果您仔细阅读了答案,我已经重复了与elementalselenium相同的测试,这是经常引用的少数几个可用比较之一,使用的是相同的目标和定位器,但使用的浏览器为5年。
Todor Minakov

3
@TodorMinakov很棒的帖子!!!我100%同意,我也认为XPath语法也更自然(至少对我而言),因为它类似于我们都非常了解的东西。那就是文件/文件夹路径。因此,我认为一个对CSS或XPath知识为零的人会更容易学习XPath。由于性能差异可以忽略不计,因此我认为学习曲线值得考虑。
hfontanez

1
谢谢@hfontanez; 与文件系统结构非常相似,我还没有想到。不过,对于步入的便利性,我不得不不同意一点-XPath语法起初可能有点令人生畏,加上它有一些陷阱(例如,[]后面//的索引)。但是在学习和使用它的第一天之后,几乎每个人都越过了学习曲线的临界点:) (恕我直言,css的介入更容易)
Todor Minakov,

13

cssSelectorXPath之间的辩论将仍然是Selenium社区中最主观的辩论之一。到目前为止,我们已经可以概括为:

  • 支持cssSelector的人们说它更具可读性和更快性(尤其是在Internet Explorer上运行时)。
  • 尽管那些支持XPath的人吹捧它具有横穿页面的能力(而cssSelector却不能)。
  • 在IE8等较旧的浏览器中遍历DOM不适用于cssSelector,但对于XPath则很好。
  • XPath可以遍历DOM(例如从子级到父级),而cssSelector只能遍历DOM(例如从父级到子级)
  • 但是,在较旧的浏览器中无法使用cssSelector遍历DOM不一定是一件坏事,因为它更多地表明您的页面设计不良,并且可以从一些有用的标记中受益。
  • Ben Burton提到您应该使用cssSelector,因为这是构建应用程序的方式。这使测试更容易编写,讨论和帮助其他人维护。
  • Adam Goucher表示将采用一种更加混合的方法-首先关注ID,然后关注cssSelector,仅在需要时才使用XPath(例如,沿DOM前进),并且XPath对于高级定位器将始终更加强大。

Dave Haeffner具有两个HTML数据表的页面上进行了测试,其中一个表编写时没有有用的属性(IDClass),而另一个表则没有帮助。我在讨论中详细分析了测试过程和该实验的结果,为什么我应该使用cssSelector选择器而不是XPath进行自动测试?。尽管该实验表明每种定位器策略在浏览器中都相当等效,但它并不能为我们充分描绘出整体情况。戴夫·哈夫纳Dave Haeffner)在其他讨论中X路径,在显微镜下提到,在端到端测试中,Sauce启动浏览器启动以及被测试应用程序之间的等待时间还有很多其他变量。该实验的不幸结果可能是一个驱动程序可能比另一个驱动程序更快(例如IE vs Firefox),而实际上并非如此。真正了解cssSelectorXPath之间的性能差异是什么,我们需要更深入地研究。我们通过使用性能基准测试实用程序在本地计算机上运行所有程序来做到这一点。我们还专注于特定的Selenium动作,而不是整个测试运行,并且运行了无数次。我已经在讨论硒的cssSelector与XPath的讨论中详细分析了具体的测试程序和该实验的结果。但是测试仍然缺少一个方面,即更多的浏览器覆盖范围(例如Internet Explorer 9和10)以及针对更大更深页面的测试。

戴夫·哈夫纳Dave Haeffner)在另一个讨论中提到X路径,在显微镜下(第2部分),为了确保以最佳的方式覆盖了所需的基准,我们需要考虑一个展示大而深的页面的示例


测试设置

为了演示这个详细的示例,安装了Windows XP虚拟机并安装了Ruby(1.9.3)。还安装了所有可用的Selenium浏览器及其等效的浏览器驱动程序。为了进行基准测试,使用了Ruby的标准库benchmark


测试代码

require_relative 'base'
require 'benchmark'

class LargeDOM < Base

  LOCATORS = {
    nested_sibling_traversal: {
      css: "div#siblings > div:nth-of-type(1) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3)",
      xpath: "//div[@id='siblings']/div[1]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]"
    },
    nested_sibling_traversal_by_class: {
      css: "div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1",
      xpath: "//div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]"
    },
    table_header_id_and_class: {
      css: "table#large-table thead .column-50",
      xpath: "//table[@id='large-table']//thead//*[@class='column-50']"
    },
    table_header_id_class_and_direct_desc: {
      css: "table#large-table > thead .column-50",
      xpath: "//table[@id='large-table']/thead//*[@class='column-50']"
    },
    table_header_traversing: {
      css: "table#large-table thead tr th:nth-of-type(50)",
      xpath: "//table[@id='large-table']//thead//tr//th[50]"
    },
    table_header_traversing_and_direct_desc: {
      css: "table#large-table > thead > tr > th:nth-of-type(50)",
      xpath: "//table[@id='large-table']/thead/tr/th[50]"
    },
    table_cell_id_and_class: {
      css: "table#large-table tbody .column-50",
      xpath: "//table[@id='large-table']//tbody//*[@class='column-50']"
    },
    table_cell_id_class_and_direct_desc: {
      css: "table#large-table > tbody .column-50",
      xpath: "//table[@id='large-table']/tbody//*[@class='column-50']"
    },
    table_cell_traversing: {
      css: "table#large-table tbody tr td:nth-of-type(50)",
      xpath: "//table[@id='large-table']//tbody//tr//td[50]"
    },
    table_cell_traversing_and_direct_desc: {
      css: "table#large-table > tbody > tr > td:nth-of-type(50)",
      xpath: "//table[@id='large-table']/tbody/tr/td[50]"
    }
  }

  attr_reader :driver

  def initialize(driver)
    @driver = driver
    visit '/large'
    is_displayed?(id: 'siblings')
    super
  end

  # The benchmarking approach was borrowed from
  # http://rubylearning.com/blog/2013/06/19/how-do-i-benchmark-ruby-code/
  def benchmark
    Benchmark.bmbm(27) do |bm|
      LOCATORS.each do |example, data|
    data.each do |strategy, locator|
      bm.report(example.to_s + " using " + strategy.to_s) do
        begin
          ENV['iterations'].to_i.times do |count|
         find(strategy => locator)
          end
        rescue Selenium::WebDriver::Error::NoSuchElementError => error
          puts "( 0.0 )"
        end
      end
    end
      end
    end
  end

end

结果

注意:输出以秒为单位,结果为100次执行的总运行时间。

以表格形式:

css_xpath_under_microscopev2

以图表形式:

图表铬

  • 火狐

图表火狐

  • Internet Explorer 8

图表-ie8

  • Internet Explorer 9

图表-ie9

  • Internet Explorer 10

图表-ie10

  • 歌剧

图表歌剧


分析结果

  • Chrome和Firefox显然已经过调整,可以提高cssSelector的性能。
  • Internet Explorer 8是无法使用的cssSelector的抓包,无法控制的XPath遍历大约需要65秒,并且需要38秒的表遍历而没有cssSelector进行比较。
  • 在IE 9和10中,XPath总体上更快。在Safari中,除了使用XPath进行一些较慢的遍历之外,其他方面都是一个难题。在几乎所有浏览器中,使用XPath完成的嵌套同级遍历表单元遍历都是一项昂贵的操作。
  • 这些不足为奇,因为定位器脆弱且效率低下,我们需要避免使用它们。

概要

  • 总体而言,在两种情况下XPath明显比cssSelector慢。但是它们很容易避免。
  • 性能差异略为有利 适用于非IE浏览器,并且略微赞成 用于IE浏览器。

琐事

您可以使用该库自行执行基准测试,Dave Haeffner打包了所有代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.