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

Che*_*tan 71 selenium xpath webdriver css-selectors selenium-webdriver

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

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

提前感谢您的宝贵建议.

nil*_*esh 87

CSS选择器的性能远远优于Xpath,它在Selenium社区中得到了很好的记录.这是一些原因,

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

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

你可以在这里阅读Simon的博客.他还建议使用CSS而不是Xpath.

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

编辑1

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

  • @Nilesh:我不同意你的回答.1.)每个浏览器中的CSS引擎也不同.这不是一个论点.3.)凭借一些经验,XPath非常容易理解,并提供比CSS更多的功能.如果搜索非常嵌套的元素,它们都很复杂:XPath和CSS.根据我的经验,对这个问题的任何一般性答案都是错误的.决定CSS/XPATH必须单独进行.最初的问题是关于表现.你的答案主要包括假设和个人意见.一个真正的证明是测量性能并在此处发布结果. (7认同)
  • CSS选择器不允许文本.'contains'在CSS中已弃用.就像我上面说的那样,选择器与内容无关.内容可以驻留在外面.您可以与开发人员交谈.他们必须将文本外化.他们大多数时候都在每个地方都有字典.因此字典中的键是相同的,但值根据区域设置而变化.您可以使用这些文件来验证内容.请注意,您需要使用JDK中的nativ2ascii工具将本机字符转换为ascii字符.我必须写一篇博客.我使用这种技术测试了许多语言环境. (6认同)
  • 一篇很好的文章与您的第一句话矛盾:“ CSS选择器的性能远胜于Xpath”。并不是那么简单,甚至可能相反。并且:“ IE没有本机的xpath引擎,因此硒会注入自己的xpath引擎以实现其API的兼容性。” 硒在这里存在设计错误。当然,最好用C ++而不是Java脚本来实现XPath引擎。但是IE已死,现在是Edge。在所有性能问题的背后,一定不要忘记CSS缺少非常重要的功能,例如搜索元素的文本。 (2认同)
  • http://elementalselenium.com/tips/32-xpath-vs-css提供了基准测试,表明css3不再明显更快. (2认同)

Tod*_*kov 31

我要保持不受欢迎的SO硒标签认为XPATH 最好到,从长远来看CSS.

让我先解决"房间里的大象" - xpath比css慢.

使用当前的cpu功率(读取:过去5年中生产的任何东西),甚至是browserstack/saucelabs虚拟机,以及浏览器的开发(阅读:过去5年中所有流行的浏览器),情况并非如此.浏览器的引擎已经开发出来了,xpath的支持是统一的,IE是不合适的(希望对我们大多数人来说).在另一个答案中的这种比较被引用到了所有地方,但它是非常上下文的 - 有多少人在运行 - 或关心 - 反对IE8?

如果存在差异,则只需几分之一毫秒.

然而,大多数更高级别的框架在原始selenium调用上增加了至少1ms的开销(包装器,处理程序,状态存储等); 我选择的个人武器 - 机器人框架 - 增加至少2毫秒,我非常乐意为它所提供的东西做出牺牲.从AWS us-east-1到BrowserStack的集线器的网络往返通常是11毫秒.

因此,对于远程浏览器,如果xpath和css之间存在差异,那么其他所有内容都会黯然失色.


没有那么多的公开比较(我真的看过引用的那个),所以 - 这是一个粗略的单例,虚拟和简单的.目标 - 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))
Run Code Online (Sandbox Code Playgroud)

对于那些不熟悉python的人 - 它打开页面,找到元素 - 首先使用css定位器,然后使用xpath; 查找操作重复1,000次.输出是1,000次重复的总时间(秒),以及一次查找的平均时间(以毫秒为单位).

定位器是 - 对于xpath - "具有这个确切类值的div元素,在DOM中的某个位置"; css类似 - "这个类的div元素,在DOM中的某个地方".故意选择不要过度调整; 此外,类选择器被引用为css为"id之后的第二快".

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

这是输出:

css总时间1000次重复:8.84s,每次发现:8.84ms

xpath 1000次重复的总时间:8.52s,每次查找:8.52ms

显然,每个发现的时间非常接近; 差为0 0.32 毫秒.不要跳 - xpath更快 - 有时它是,有时它是css.

让我们尝试使用另一组定位器,这是一个更复杂的定位器 - 一个具有子串的属性(至少对我来说是常见的方法,当一个元素的一部分具有功能意义时,它会追随元素的类):

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

xpath total time for 1000 repeats: 8.52s, per find: 8.52ms
Run Code Online (Sandbox Code Playgroud)

这两个定位器在语义上也是相同的 - "找到一个div元素,在它的class属性中有这个子串".结果如下:

css总时间1000次重复:8.60s,每次发现:8.60ms

xpath总重复时间1000次:8.75s,每次查找:8.75ms

差值为0.15ms.

使用更复杂的DOM,结果是一样的; 我手边没有任何可公开提供的URL来举例说明,但是再次看到xpath和css的类似结果.


作为练习 - 评论/其他答案中的链接博客相同的测试- 测试页面是公开的,测试代码也是如此.

他们在代码中执行了一些操作 - 单击列以对其进行排序,然后获取值,并检查UI排序是否正确.我会削减它 - 只需得到定位器,毕竟这是根测试,对吧?

与上面相同的代码,包括以下变化:

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

第一个定位器 - "按ID和类查找元素" - 是:

xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'
Run Code Online (Sandbox Code Playgroud)

结果:

css总时间1000次重复:8.24s,每次发现:8.24ms

xpath总重复时间为1000次:8.45s,每次查找:8.45ms

差值为0.2毫秒.

"通过遍历寻找元素":

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

xpath total time for 1000 repeats: 8.75s, per find: 8.75ms
Run Code Online (Sandbox Code Playgroud)

结果:

css总时间1000次重复:9.29s,每次发现:9.29ms

xpath 1000次重复的总时间:8.79s,每次查找:8.79ms

这次是0.5毫秒(相反,xpath在这里变得"更快").


那么,出于xpath和css,两者中哪一个选择性能?答案很简单 - 选择id来定位.

简而言之,如果元素的id是唯一的(因为它应该根据规范),它的值在浏览器的DOM内部表示中起着重要作用,因此通常是最快的.


随着性能的提升,为什么我认为xpath更好?简单 - 通用性和动力.

Xpath是为处理XML文档而开发的语言; 因此,它允许比css更强大的构造.
例如,在树中的每个方向导航 - 找到一个元素,然后转到其祖父母并搜索具有某些属性的子元素.它允许嵌入式布尔条件 - cond1而不是cond2或不cond3(cond3和cond4)); 嵌入式选择器 - "找到具有这些属性的div,然后根据它进行导航".它允许基于节点的值进行搜索 - 但是对这种做法不满意,它确实派上用场,特别是在结构错误的文档中(没有确定的属性可以踩,如动态id和类).

css的踩踏肯定更容易 - 人们可以在几分钟内开始编写选择器; 但经过几天的使用,xpath的功能和可能性很快就克服了css.
纯粹是主观的 - 复杂的css比复杂的xpath表达更难阅读.

最后,再次非常主观 - 选择哪一个?恕我直言,没有正确或错误的选择 - 他们是同一问题的不同解决方案,应该选择更适合工作的任何东西.作为xpath的粉丝,我并不害羞地在我的项目中使用两者的混合 - 哎呀,有时候只要扔掉一个css就快得多,如果我知道它会做得很好.

  • @TodorMinakov很棒的帖子!!!我100%同意,我也认为XPath语法也更自然(至少对我而言),因为它类似于我们都非常了解的东西。那就是文件/文件夹路径。因此,我认为一个对CSS或XPath知识为零的人会更容易学习XPath。由于性能差异可以忽略不计,因此我认为学习曲线值得考虑。 (3认同)
  • 对于您的第一个问题 - 答案中提供了 DOM 的屏幕截图,并且该页面在线且公开。对于第一个和第二个,如果您仔细阅读答案,我已经重复了与 elementalselenium 相同的测试,这是为数不多的经常被引用的可用比较之一,使用与它们相同的目标和定位器,但仅使用 5 年更新的浏览器. (2认同)

Deb*_*anB 9

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

  • 支持cssSelector的人们说它更具可读性和速度(特别是在Internet Explorer上运行时)。
  • 尽管那些支持XPath的人吹捧它具有横穿页面的能力(而cssSelector却不能)。
  • 在IE8之类的旧版浏览器中遍历DOM不能与cssSelector一起使用,但可以与XPath一起使用
  • XPath可以遍历DOM(例如从子级到父级),而cssSelector只能遍历DOM(例如从父级到子级)
  • 但是,在旧版浏览器中无法使用cssSelector遍历DOM 不一定是一件坏事,因为它更多地表明您的页面设计不良,并且可以从有用的标记中受益。
  • Ben Burton提到您应该使用cssSelector,因为这是构建应用程序的方式。这使测试更易于编写,讨论和帮助其他人维护。
  • 亚当古彻说,采取更为混合的方法-首先关注的标识,并cssSelector,并利用XPath的,只有当你需要它(如行走了DOM)和XPath的永远是先进的定位器更强大。

Dave Haeffner具有两个HTML数据表的页面上进行了测试,其中一个表编写时没有有用的属性(IDClass),而另一个表则没有帮助。我在讨论中详细分析了测试过程和该实验的结果,为什么我应该使用cssSelector选择器而不是XPath进行自动测试?。尽管该实验表明每种“ 定位器策略”在浏览器中都相当等效,但并不能为我们充分描绘出整体情况。Dave Haeffner在其他讨论中 Css Vs中。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
Run Code Online (Sandbox Code Playgroud)

结果

注意:输出以秒为单位,结果为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和IE 10中,XPath总体上更快。在Safari中,除了使用XPath进行一些较慢的遍历之外,其他方面都是一个难题。在几乎所有浏览器中,使用XPath进行的嵌套同级遍历表单元遍历都是一项昂贵的操作。
  • 这些不足为奇,因为定位器脆弱且效率低下,我们需要避免使用它们。

摘要

  • 总体而言,在两种情况下XPath明显比cssSelector慢。但是它们很容易避免。
  • 对于非IE浏览器,性能差异稍微偏向于对于IE浏览器,稍稍偏向于

琐事

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