Mam zamiar trzymać się niepopularnej opinii w tagach selenu SO, że XPath jest lepszy niż CSS w dłuższej perspektywie.
Ten długi post ma dwie sekcje - najpierw umieszczę dowód z tyłu serwetki, że różnica w wydajności między nimi wynosi 0,1-0,3 milisekundy (tak; to 100 mikrosekund ) , a potem podzielę się opinią, dlaczego XPath jest potężniejszy.
Różnica w wydajności
Najpierw zajmijmy się „słoniem w pokoju” - ta ścieżka jest wolniejsza niż css.
Przy obecnej mocy procesora (czytaj: cokolwiek x86 wyprodukowano od 2013 roku) , nawet na maszynach wirtualnych browserstack / saucelabs / aws i rozwoju przeglądarek (czytaj: wszystkie popularne w ciągu ostatnich 5 lat) to prawie nie ma miejsca. Silniki przeglądarki rozwinęły się, wsparcie dla xpath jest jednolite, IE nie ma na myśli (miejmy nadzieję, że dla większości z nas) . To porównanie w drugiej odpowiedzi jest przytaczane wszędzie, ale jest bardzo kontekstowe - ilu z nich działa - lub obchodzi - automatyzację przeciwko IE8?
Jeśli jest różnica, jest to ułamek milisekundy .
Jednak większość frameworków wyższego poziomu i tak dodaje co najmniej 1 ms narzutu nad wywołaniem surowego selenu (opakowania, programy obsługi, przechowywanie stanu itp.); moja ulubiona broń - RobotFramework - dodaje co najmniej 2 ms, które z przyjemnością poświęcę dla tego, co zapewnia. Podróż w obie strony z AWS us-east-1 do centrum BrowserStack trwa zwykle 11 milisekund .
Tak więc w przypadku zdalnych przeglądarek, jeśli istnieje różnica między xpath i css, jest ona przyćmiona przez wszystko inne, pod względem wielkości.
Pomiary
Nie ma zbyt wielu porównań publicznych (tak naprawdę widziałem tylko cytowane) , więc - oto przybliżony pojedynczy przypadek, atrapa i prosty.
Lokalizuje element za pomocą dwóch strategii X razy i porównuje średni czas dla tego.
Cel - strona docelowa BrowserStack i jej przycisk „Zarejestruj się”; zrzut ekranu HTML podczas pisania tego postu:
Oto kod testowy (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))
Dla niezaznajomionych z Pythonem - otwiera stronę i wyszukuje element - najpierw z lokalizatorem css, potem z xpath; operacja wyszukiwania jest powtarzana 1000 razy. Dane wyjściowe to całkowity czas w sekundach dla 1000 powtórzeń i średni czas jednego znalezienia w milisekundach.
Lokalizatory to:
- for xpath - "element div mający dokładnie taką wartość klasy, gdzieś w DOM";
- css jest podobny - „element div z tą klasą, gdzieś w DOM”.
Celowo wybrany, aby nie być przestrojonym; również selektor klasy jest cytowany w css jako „drugi najszybszy po id”.
Środowisko - Chrome v66.0.3359.139, chromedriver v2.38, cpu: ULV Core M-5Y10 zwykle pracujący z częstotliwością 1,5 GHz (tak, "edytor tekstu", nawet nie zwykła bestia i7) .
Oto wynik:
css total time 1000 repeats: 8.84s, per find: 8.84ms
xpath total time for 1000 repeats: 8.52s, per find: 8.52ms
Oczywiście czasy na znalezienie są dość bliskie; różnica wynosi 0,32 milisekundy . Nie przeskakuj "xpath jest szybszy" - czasami tak jest, czasami jest to css.
Spróbujmy z innym zestawem lokalizatorów, trochę bardziej skomplikowanym - atrybutem posiadającym podciąg (powszechne podejście przynajmniej dla mnie, podążanie za klasą elementu, gdy jego część ma znaczenie funkcjonalne) :
xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'
Te dwa lokalizatory są ponownie semantycznie takie same - „znajdź element div mający w swojej klasie atrybut ten podciąg”.
Oto wyniki:
css total time 1000 repeats: 8.60s, per find: 8.60ms
xpath total time for 1000 repeats: 8.75s, per find: 8.75ms
Diff z 0.15ms .
Jako ćwiczenie - ten sam test, co w przypadku linku na blogu w komentarzach / innej odpowiedzi - strona testowa jest publiczna, podobnie jak kod testowy .
Robią kilka rzeczy w kodzie - klikają kolumnę, aby posortować według niej, następnie pobierają wartości i sprawdzają, czy sortowanie w interfejsie użytkownika jest poprawne.
Wytnę to - w końcu weź lokalizatory - to jest test rootowania, prawda?
Ten sam kod co powyżej, z tymi zmianami w:
css_locator = '#table2 tbody .dues'
xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
A oto wynik:
css total time 1000 repeats: 8.24s, per find: 8.24ms
xpath total time for 1000 repeats: 8.45s, per find: 8.45ms
Różnica 0,2 milisekundy.
„Znajdowanie elementów przez przechodzenie”:
css_locator = '#table1 tbody tr td:nth-of-type(4)'
xpath_locator = "//table[@id='table1']//tr/td[4]"
Wynik:
css total time 1000 repeats: 9.29s, per find: 9.29ms
xpath total time for 1000 repeats: 8.79s, per find: 8.79ms
Tym razem jest to 0,5 ms (odwrotnie, xpath okazał się tutaj „szybszy”).
Tak więc 5 lat później (lepsze silniki przeglądarek) i skupiając się tylko na wydajności lokalizatorów (brak działań typu sortowanie w interfejsie użytkownika itp.), To samo stanowisko testowe - praktycznie nie ma różnicy między CSS a XPath.
A więc z xpath i css, który z dwóch wybrać pod kątem wydajności? Odpowiedź jest prosta - wybierz lokalizację według identyfikatora .
Krótko mówiąc, jeśli identyfikator elementu jest unikalny (zgodnie ze specyfikacją), jego wartość odgrywa ważną rolę w wewnętrznej reprezentacji DOM w przeglądarce i dlatego jest zwykle najszybszy.
Jednak unikalne i stałe (np. Nie generowane automatycznie) identyfikatory nie zawsze są dostępne, co prowadzi nas do pytania „dlaczego XPath, skoro istnieje CSS?”
Zaleta XPath
Skoro wydajność nie jest widoczna, dlaczego myślę, że xpath jest lepszy? Prostota - wszechstronność i moc.
Xpath to język opracowany do pracy z dokumentami XML; jako taka pozwala na znacznie potężniejsze konstrukcje niż css.
Na przykład nawigacja w każdym kierunku w drzewie - znajdź element, następnie przejdź do jego dziadka i poszukaj jego dziecka o określonych właściwościach.
Pozwala na osadzone warunki boolowskie - cond1 and not(cond2 or not(cond3 and cond4))
; wbudowane selektory - „znajdź element div mający te elementy podrzędne z tymi atrybutami, a następnie przejdź zgodnie z nim”.
XPath umożliwia wyszukiwanie w oparciu o wartość węzła (jego tekst) - jakkolwiek jest to niezadowolone z tej praktyki, przydaje się, zwłaszcza w dokumentach o złej strukturze (brak określonych atrybutów do nadepnięcia, takich jak dynamiczne identyfikatory i klasy - zlokalizuj element według tekstu zawartość) .
Wkraczanie w css jest zdecydowanie łatwiejsze - można zacząć pisać selektory w ciągu kilku minut; ale po kilku dniach użytkowania, moc i możliwości xpath szybko pokonuje css.
I czysto subiektywne - złożony css jest znacznie trudniejszy do odczytania niż złożone wyrażenie xpath.
Outro;)
Wreszcie znowu bardzo subiektywne - który wybrać?
IMO, nie ma dobrego lub złego wyboru - są to różne rozwiązania tego samego problemu i należy wybrać to, co jest bardziej odpowiednie do pracy.
Będąc „fanem” XPath, nie wstydzę się używać w swoich projektach mieszanki obu - do diabła, czasami o wiele szybciej jest po prostu wrzucić CSS, jeśli wiem, że zadziała dobrze.