Terrò l'impopolare opinione sui tag di selenio SO che XPath è preferibile ai CSS nel lungo periodo.
Questo lungo post ha due sezioni: prima metto una prova sul retro del tovagliolo, la differenza di prestazioni tra i due è di 0,1-0,3 millisecondi (sì, sono 100 micro secondi) , e poi condividerò la mia opinione sul perché XPath è più potente.
Differenza di prestazioni
Per prima cosa affrontiamo "l'elefante nella stanza": xpath è più lento di css.
Con l'attuale potenza della cpu (leggi: qualsiasi cosa x86 prodotta dal 2013) , anche su VM browserstack / saucelabs / aws, e lo sviluppo dei browser (leggi: tutti quelli popolari negli ultimi 5 anni) non è certo il caso. I motori del browser si sono sviluppati, il supporto di xpath è uniforme, IE è fuori dai giochi (si spera per la maggior parte di noi) . Questo confronto nell'altra risposta viene citato ovunque, ma è molto contestuale: quanti sono in esecuzione o si preoccupano per l'automazione contro IE8?
Se c'è una differenza, è in un file frazione di millisecondo .
Tuttavia, la maggior parte dei framework di livello superiore aggiunge comunque almeno 1 ms di overhead sulla chiamata al selenio grezzo (wrapper, gestori, memorizzazione dello stato ecc.); la mia arma preferita - RobotFramework - aggiunge almeno 2 ms, che sono più che felice di sacrificare per ciò che fornisce. Un viaggio di andata e ritorno di rete da un AWS us-east-1 all'hub di BrowserStack è in genere di 11 millisecondi .
Quindi con i browser remoti se c'è una differenza tra xpath e css, viene messa in ombra da tutto il resto, in ordini di grandezza.
Le misurazioni
Non ci sono molti confronti pubblici (in realtà ho visto solo quello citato) , quindi eccone un caso singolo approssimativo, fittizio e semplice.
Individuerà un elemento in base alle due strategie X volte e confronterà il tempo medio per quello.
Il target: la pagina di destinazione di BrowserStack e il relativo pulsante "Registrati"; uno screenshot dell'html mentre scriveva questo post:
Ecco il codice di test (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))
Per chi non ha familiarità con Python - apre la pagina e trova l'elemento - prima con il css locator, poi con xpath; l'operazione di ricerca viene ripetuta 1.000 volte. L'output è il tempo totale in secondi per le 1.000 ripetizioni e il tempo medio per una ricerca in millisecondi.
I localizzatori sono:
- per xpath - "un elemento div avente questo valore di classe esatto, da qualche parte nel DOM";
- il css è simile: "un elemento div con questa classe, da qualche parte nel DOM".
Scelto deliberatamente per non essere troppo sintonizzato; inoltre, il selettore di classe è citato per il css come "il secondo più veloce dopo un id".
L'ambiente - Chrome v66.0.3359.139, chromedriver v2.38, cpu: ULV Core M-5Y10 normalmente in esecuzione a 1,5 GHz (sì, un "word-processing", nemmeno un normale i7 beast) .
Ecco l'output:
css total time 1000 repeats: 8.84s, per find: 8.84ms
xpath total time for 1000 repeats: 8.52s, per find: 8.52ms
Ovviamente i tempi per ritrovamento sono piuttosto vicini; la differenza è di 0,32 millisecondi . Non saltare "xpath è più veloce" - a volte lo è, a volte è css.
Proviamo con un altro set di locatori, un po 'più complicato - un attributo con una sottostringa (approccio comune almeno per me, che segue la classe di un elemento quando una parte di essa ha un significato funzionale) :
xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'
I due locatori sono di nuovo semanticamente uguali - "trova un elemento div che ha nel suo attributo di classe questa sottostringa".
Ecco i risultati:
css total time 1000 repeats: 8.60s, per find: 8.60ms
xpath total time for 1000 repeats: 8.75s, per find: 8.75ms
Diff di 0,15 ms .
Come esercizio - lo stesso test eseguito nel blog collegato nei commenti / altra risposta - la pagina di test è pubblica, così come il codice di test .
Stanno facendo un paio di cose nel codice: fare clic su una colonna per ordinare in base ad essa, quindi ottenere i valori e controllare che l'ordinamento dell'interfaccia utente sia corretto.
Lo taglierò - prendi solo i localizzatori, dopotutto - questo è il test di root, giusto?
Lo stesso codice di cui sopra, con queste modifiche in:
css_locator = '#table2 tbody .dues'
xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
Ed ecco il risultato:
css total time 1000 repeats: 8.24s, per find: 8.24ms
xpath total time for 1000 repeats: 8.45s, per find: 8.45ms
Diff di 0,2 millisecondi.
Il "Trovare elementi attraversando":
css_locator = '#table1 tbody tr td:nth-of-type(4)'
xpath_locator = "//table[@id='table1']//tr/td[4]"
Il risultato:
css total time 1000 repeats: 9.29s, per find: 9.29ms
xpath total time for 1000 repeats: 8.79s, per find: 8.79ms
Questa volta è di 0,5 ms (al contrario, xpath è risultato "più veloce" qui).
Quindi 5 anni dopo (motori di browser migliori) e concentrandosi solo sulle prestazioni dei localizzatori (nessuna azione come l'ordinamento nell'interfaccia utente, ecc.), Lo stesso banco di prova: non c'è praticamente alcuna differenza tra CSS e XPath.
Quindi, al di fuori di xpath e css, quale dei due scegliere per le prestazioni? La risposta è semplice: scegli la localizzazione per id .
Per farla breve, se l'id di un elemento è unico (come dovrebbe essere secondo le specifiche), il suo valore gioca un ruolo importante nella rappresentazione interna del DOM del browser, e quindi di solito è il più veloce.
Tuttavia, gli ID univoci e costanti (ad esempio non generati automaticamente) non sono sempre disponibili, il che ci porta a "perché XPath se c'è CSS?"
Il vantaggio di XPath
Con le prestazioni fuori dall'immagine, perché penso che xpath sia migliore? Semplice: versatilità e potenza.
Xpath è un linguaggio sviluppato per lavorare con documenti XML; in quanto tale, consente costrutti molto più potenti di css.
Ad esempio, la navigazione in ogni direzione dell'albero: trova un elemento, quindi vai dai suoi nonni e cerca un suo figlio con determinate proprietà.
Consente condizioni booleane incorporate - cond1 and not(cond2 or not(cond3 and cond4))
; selettori incorporati - "trova un div che ha questi figli con questi attributi, e poi naviga in base ad esso".
XPath consente la ricerca in base al valore di un nodo (il suo testo) - per quanto questa pratica sia disapprovata, è utile soprattutto nei documenti strutturati male (nessun attributo definito su cui calpestare, come ID dinamici e classi - individua l'elemento in base al suo testo contenuto) .
Il passaggio in CSS è decisamente più semplice: si può iniziare a scrivere selettori in pochi minuti; ma dopo un paio di giorni di utilizzo, la potenza e le possibilità di xpath superano rapidamente css.
E puramente soggettivo: un CSS complesso è molto più difficile da leggere di un'espressione xpath complessa.
Outro;)
Infine, ancora una volta molto soggettivo: quale scegliere?
IMO, non esiste una scelta giusta o sbagliata: sono soluzioni diverse allo stesso problema e dovrebbe essere scelta qualsiasi cosa sia più adatta per il lavoro.
Essendo "un fan" di XPath non sono timido nell'usare nei miei progetti un mix di entrambi - diamine, a volte è molto più veloce lanciarne uno CSS, se so che funzionerà bene.