web-dev-qa-db-ja.com

css SelectorとXpathの違いは何ですか?また、クロスブラウザーテストのパフォーマンスに関してどちらが優れていますか?

私は多言語WebアプリケーションでSelenium WebDriver 2.25.0を使用しており、主にページコンテンツをテストしています(アラビア語、英語、ロシア語などの異なる言語用)。

パフォーマンスに応じて優れたアプリケーションの場合、すべてのブラウザをサポートする必要があります(つまり、IE 7,8,9、FF、Chromeなど)。

貴重なご提案を事前に感謝します。

64
Chetan

CSSセレクターのパフォーマンスはXpathよりもはるかに優れており、Seleniumコミュニティで十分に文書化されています。いくつかの理由があります、

  • Xpathエンジンはブラウザごとに異なるため、一貫性がありません
  • IEにはネイティブxpathエンジンがないため、SeleniumはAPIの互換性のために独自のxpathエンジンを挿入します。したがって、WebDriverが本質的に促進するネイティブブラウザー機能を使用する利点を失います。
  • Xpathは複雑になる傾向があるため、私の意見では読みにくい

ただし、親要素を検索したり、テキストで要素を検索したりするなど、xpathを使用する必要がある場合があります(後者はお勧めしません)。

Simon here からブログを読むことができます。また、XpathよりもCSSを推奨しています。

コンテンツをテストする場合は、要素のコンテンツに依存するセレクターを使用しないでください。それはすべてのロケールのメンテナンスの悪夢です。開発者と話をして、辞書やリソースバンドルなど、アプリケーションでテキストを外部化するために使用した手法を使用してみてください。詳細を説明する blog .

編集1

@parishodakのおかげで、CSSのパフォーマンスが優れていることを証明する数値を提供する link があります。

81
nilesh

長期的にはCSSに対してXPathのほうが望ましいというSO Seleniumタグの意見については、人気がありません。

この長い投稿には2つのセクションがあります-最初に、ナプキンの裏側に、2つのパフォーマンスの違いが.1-0.3ミリ秒(yes ;それは100 micro seconds)であり、XPathがより強力である理由を共有します。


パフォーマンスの違い

最初に「部屋の象」に取り組みましょう。xpathはcssよりも遅いです。

現在のCPU能力(2013年以降に作成されたx86のすべて)、browserstack/saucelabs/aws VM、およびブラウザの開発(読み取り:過去5年間で人気のあるすべてのもの)それはほとんどありません。ブラウザのエンジンが開発されました。xpathのサポートは統一されており、IEは画像の外にあります(うまくいけばほとんどの人にとって)。他の答えのこの比較はあちこちで引用されていますが、IE8に対する自動化は非常に文脈的で、何人が実行されていますか?

差がある場合、ミリ秒の小数部にあります。

しかし、ほとんどの高レベルのフレームワークは、未加工のSelenium呼び出し(ラッパー、ハンドラー、状態の保存など)に少なくとも1msのオーバーヘッドを追加します。私が選んだ武器-RobotFramework-は少なくとも2msを追加しますが、それが提供するものを犠牲にして喜んでいます。 AWS us-east-1からBrowserStackのハブへのネットワークラウンドトリップは、通常11ミリ秒です。

そのため、リモートブラウザの場合、xpathとcssの間に違いがあると、他のすべての要素によって桁違いに隠れてしまいます。


測定

それほど多くの公開比較はありません(私は実際に引用されたものだけを見ました)–だから–ここでは大まかな単一のケース、ダミーのシンプルなものです。
2つの戦略によってX回要素を見つけ、その平均時間を比較します。

ターゲット– BrowserStackのランディングページ、およびその「サインアップ」ボタン。この投稿を書いているhtmlのスクリーンショット:

enter image description here

テストコードは次のとおりです(python)。

from Selenium import webdriver
import timeit


if __== '__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回繰り返されます。出力は、1,000回の繰り返しの合計時間(秒単位)と、1回の検索の平均時間(ミリ秒単位)です。

ロケーターは次のとおりです。

  • for xpath –「DOMのどこかに、この正確なクラス値を持つdiv要素」。
  • cssも同様です-「DOMのどこかにあるこのクラスのdiv要素」。

意図的にオーバーチューニングされないように選択されています。また、クラスセレクターはcssに対して「idに続く2番目に速い」として引用されています。

環境-Chrome v66.0.3359.139、chromedriver v2.38、cpu:通常1.5GHzで実行されるULVコアM-5Y10(はい、「ワープロ「1つ、通常のi7獣でもない)

出力は次のとおりです。

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

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

明らかに、検索ごとのタイミングはかなり近いです。違いは.32millisecondsです。 「xpathの方が速い」というジャンプは避けてください-場合によってはcssです。


もう少し複雑なロケーターを試してみましょう。サブストリング(属性の一部が機能的な意味を持つ場合、少なくとも私にとっては一般的なアプローチです。 )

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

2つのロケーターも意味的に同じです-「クラス属性にこのサブストリングを持つdiv要素を見つけます」。
結果は次のとおりです。

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

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

.15msの差分。


演習として-コメント/その他の回答の リンクされたブログ で行われたのと同じテスト- テストページ は公開されているため、 テストコード

彼らはコードでいくつかのことを行っています-列をクリックしてソートし、値を取得し、UIのソートが正しいことを確認します。
切り取ります-ロケーターを取得するだけです、結局のところ-これはルートテストですよね?

上記と同じコード。これらの変更は次のとおりです。

  • URLは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

.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

今回は.5 msです(逆に、xpathはここで「高速」になりました)。

5年後(より良いブラウザーエンジン)andロケーターのパフォーマンスのみ(UIでの並べ替えなどのアクションはなし)、同じテストベッド-実際にはありませんCSSとXPathの違い。


では、xpathとcssのうち、どちらをパフォーマンスのために選択するのでしょうか?答えは簡単です。id by by idを選択してください。

要するに、要素のidが一意である場合(仕様に従っていると想定される場合)、その値はブラウザーのDOMの内部表現で重要な役割を果たし、したがって通常は最速です。

しかし、一意で一定の(たとえば、自動生成されない)idは常に使用できるとは限らないため、「CSSがあるのにXPathが必要な理由」につながります。


XPathの利点

パフォーマンスが見えないのに、なぜxpathの方が優れていると思いますか?シンプル–汎用性とパワー。

Xpathは、XMLドキュメントを操作するために開発された言語です。そのため、cssよりもはるかに強力な構成が可能です。
たとえば、ツリー内のあらゆる方向へのナビゲーション–要素を見つけ、その祖父母に移動して、特定のプロパティを持つその子を検索します。
埋め込みブール条件を許可します– cond1 and not(cond2 or not(cond3 and cond4));埋め込みセレクタ-「これらの属性を持つこれらの子を持つdivを見つけて、それに従ってナビゲートします」。
XPathは、ノードの値(テキスト)に基づいて検索を許可します。ただし、このプラクティスに眉をひそめたとしても、特に構造の悪いドキュメントでは役立ちます(明確な属性なしon、動的IDやクラスのように、テキストコンテンツで要素を見つけます)

Cssのステップインは間違いなく簡単です。数分でセレクターの作成を開始できます。しかし、数日間使用した後、xpathの威力と可能性はすぐにCSSを克服しました。
そして純粋に主観的–複雑なcssは、複雑なxpath式よりも読みにくいです。

アウトロ;)

最後に、再び非常に主観的-どちらを選択しますか?

私見、正しいまたは間違った選択はありません-それらは同じ問題に対する異なる解決策であり、仕事により適したものは何でも選ぶべきです。

XPathの「ファン」であるため、私のプロジェクトで両方を組み合わせて使用​​することを恥ずかしがり屋ではありません。

23
Todor Minakov

cssSelector vs XPathの間の議論は、Selenium Communityで最も主観的な議論の1つとして残っています。これまでにすでにわかっていることは、次のように要約できます。

  • 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実行 test on 2つのHTMLデータテーブルがあるページ 、 1つのテーブルは有用な属性(IDおよびClass)なしで記述されており、もう1つのテーブルは属性付きです。 テスト手順と、この実験の結果を詳細に議論しました 自動テストにXPathではなくcssSelectorセレクターを使用する理由は何ですか? 。この実験は、各 Locator Strategy がブラウザー間で合理的に同等であることを実証しましたが、全体像を適切にペイントできませんでした。 Dave Haeffner他の議論で Css Vs. X Path、Under a Microscope 言及されましたが、エンドツーエンドのテストでは他にも多くの変数が使われていました- ソースの起動ブラウザの起動、およびレイテンシテスト中のアプリケーションとの間でやり取りします。その実験からの不幸な点は、実際にはそうではなかったのに、一方のドライバーが他方のドライバーよりも高速である可能性があることです(たとえばすなわち vs Firefox)まったく。 cssSelectorXPathのパフォーマンスの違いを理解するには、さらに掘り下げる必要がありました。パフォーマンスベンチマークユーティリティを使用しながら、ローカルマシンからすべてを実行することでそれを行いました。また、テスト実行全体ではなく、特定のSeleniumアクションに注目し、何度も実行しました。特定のテスト手順と、この実験の結果を詳細に議論しました cssSelector vs Xele for Selenium 。しかし、テストにはまだ1つの側面が欠けていました。つまり、より多くのブラウザのカバレッジ(たとえば、Internet Explorer 9および10)およびより大きくより深いページに対するテスト

Dave Haeffner別の議論で Css Vs. X Path、Under a Microscope(Part 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

結果

NOTE:出力は秒単位で、結果は100回の実行の合計実行時間です。

テーブル形式:

css_xpath_under_microscopev2

グラフ形式:

  • Chrome

chart-chrome

  • Firefox

chart-firefox

  • Internet Explorer 8

chart-ie8

  • Internet Explorer 9

chart-ie9

  • Internet Explorer 10

chart-ie10

  • オペラ

chart-opera


結果の分析

  • ChromeとFirefoxは、より高速cssSelectorのパフォーマンスのために明確に調整されています。
  • Internet Explorer 8は、機能しないcssSelectorのグラブバッグ、制御不能XPath〜65秒かかるトラバース、およびなしで38秒のテーブルトラバース- cssSelector結果と比較します。
  • IE 9および10では、XPathは全体的に高速です。 Safariでは、XPathを使用したいくつかの低速なトラバーサル実行を除いて、これは大混乱です。そして、ほとんどすべてのブラウザーで、 ネストされた兄弟トラバーサル および テーブルセルトラバーサルXPathで実行すると、コストのかかる操作になります。
  • ロケーターは脆弱で非効率的であり、それらを避ける必要があるため、これらは驚くべきことではありません。

概要

  • 全体的に、XPathcssSelectorより著しく遅い2つの状況があります。しかし、それらは簡単に回避できます。
  • パフォーマンスの違いは、IE以外のブラウザーでは css-selectors に、そしてIEブラウザーでは xpath にわずかに有利です。

トリビア

this library where Dave Haeffnerを使用して、すべてのコードをラップして、自分でベンチマーキングを実行できます。

7
DebanjanB