Jak wybrać określony węzeł w zestawach węzłów Xpath według indeksu za pomocą Selenium?

92

Piszę zestaw testowy Selenium. A oto wyrażenie xpath, którego używam do dopasowania wszystkich przycisków „Modyfikuj” w tabeli danych.

//img[@title='Modify']

Moje pytanie brzmi: jak mogę odwiedzić dopasowane zestawy węzłów według indeksu? Próbowałem z

//img[@title='Modify'][i]

i

//img[@title='Modify' and position() = i]

Ale to nie działa. Próbowałem też z XPath checker (jedno rozszerzenie firefox). Znaleziono łącznie 13 dopasowań, więc nie mam pojęcia, jak mam wybrać jedno z nich. A może XPath obsługuje określony wybór węzłów, które nie znajdują się w tym samym węźle nadrzędnym?

Kymair Wu
źródło

Odpowiedzi:

194

To jest FAQ :

//someName[3]

oznacza : wszystkie someNameelementy w dokumencie, które są trzecim someNamedzieckiem rodzica - takich elementów może być wiele.

To, czego chcesz, to dokładnie trzeci someNameelement :

(//someName)[3]

Objaśnienie : []ma wyższy priorytet (priorytet) niż //. Pamiętaj, aby zawsze umieszczać wyrażenia typu //someNamew nawiasach, gdy musisz określić n-ty węzeł wybranej listy węzłów.

Dimitre Novatchev
źródło
1
Dzięki wielkie! Przepraszam, całkowicie zapomniałem o rzeczach z pierwszeństwem .. Właśnie spróbowałem i działa!
Kymair Wu
1
@ Kymair-Wu: Cieszę się, że ta odpowiedź była dla Ciebie przydatna. W SO sposobem wyrażenia wdzięczności jest zaakceptowanie odpowiedzi (wskazówka: kliknij znacznik wyboru obok odpowiedzi). :)
Dimitre Novatchev
@DimitreNovatchev otrzymujesz punkty za to samo pytanie w kółko: p, dzięki za FAQ.
Eytoss
2
@Eytoss, nie ma za co. I tak, najwięcej +1 dostaję za stosunkowo proste odpowiedzi - nie za odpowiedzi, które uważam za moje największe osiągnięcia - prawdopodobnie dlatego, że wszyscy rozumieją to pierwsze i prawie nikt nie rozumie drugiego :)
Dimitre Novatchev
2
@TEHEMPRAH, Właściwie to widziałem, że w odpowiedzi nie powiedziałem „trzeciego dziecka„ someName ”jego rodzica”. Dzięki, że to zauważyłeś. Poprawione teraz.
Dimitre Novatchev
14

W iXPath nie ma .

Albo użyjesz liczb literalnych: //img[@title='Modify'][1]

Lub możesz budować ciąg wyrażenia dynamicznie: '//img[@title='Modify']['+i+']'(ale pamiętaj, że dynamiczne wyrażenia XPath nie działają z poziomu XSLT).

A może XPath obsługuje określony wybór węzłów, które nie znajdują się w tym samym węźle nadrzędnym?

Tak: (//img[@title='Modify'])[13]


Oznacza //img[@title='Modify'][i]to „dowolny <img>z tytułem„ Modyfikuj ”i elementem podrzędnym o nazwie <i>”.

Tomalak
źródło
Z jakiegoś powodu musiałem dołączyć indeks przed wyrażeniem atrybutu. Na przykład, aby znaleźć dzieci, tdktóre były szóstym dzieckiem a tri nie miały pustej treści://tr/td[6][string-length(text()) > 0]
Samir Aguiar
1
@kopranb Aby uzyskać wyjaśnienie, zobacz tę odpowiedź stackoverflow.com/a/1006439/18771
Tomalak,
Dziękuję za wyjaśnienie na temat „// img [@ title = 'Modify'] ['+ i +'] '(+1)
DebanjanB
3

Nie ma iw xpath nie jest do końca prawdą. Nadal możesz użyć, count()aby znaleźć indeks.

Rozważ następną stronę

<html>

	<head>
		<title>HTML Sample table</title>
	</head>

	<style>
	table, td, th {
		border: 1px solid black;
		font-size: 15px;
		font-family: Trebuchet MS, sans-serif;
	}
	table {
		border-collapse: collapse;
		width: 100%;
	}

	th, td {
		text-align: left;
		padding: 8px;
	}

	tr:nth-child(even){background-color: #f2f2f2}

	th {
		background-color: #4CAF50;
		color: white;
	}
	</style>

	<body>
	<table>
		<thead>
			<tr>
				<th>Heading 1</th>
				<th>Heading 2</th>
				<th>Heading 3</th>
				<th>Heading 4</th>
				<th>Heading 5</th>
				<th>Heading 6</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>Data row 1 col 1</td>
				<td>Data row 1 col 2</td>
				<td>Data row 1 col 3</td>
				<td>Data row 1 col 4</td>
				<td>Data row 1 col 5</td>
				<td>Data row 1 col 6</td>
			</tr>
			<tr>
				<td>Data row 2 col 1</td>
				<td>Data row 2 col 2</td>
				<td>Data row 2 col 3</td>
				<td>Data row 2 col 4</td>
				<td>Data row 2 col 5</td>
				<td>Data row 2 col 6</td>
			</tr>
			<tr>
				<td>Data row 3 col 1</td>
				<td>Data row 3 col 2</td>
				<td>Data row 3 col 3</td>
				<td>Data row 3 col 4</td>
				<td>Data row 3 col 5</td>
				<td>Data row 3 col 6</td>
			</tr>
			<tr>
				<td>Data row 4 col 1</td>
				<td>Data row 4 col 2</td>
				<td>Data row 4 col 3</td>
				<td>Data row 4 col 4</td>
				<td>Data row 4 col 5</td>
				<td>Data row 4 col 6</td>
			</tr>
			<tr>
				<td>Data row 5 col 1</td>
				<td>Data row 5 col 2</td>
				<td>Data row 5 col 3</td>
				<td>Data row 5 col 4</td>
				<td>Data row 5 col 5</td>
				<td>Data row 5 col 6</td>
			</tr>
			<tr>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
			</tr>
		</tbody>
	</table>

	</br>

	<table>
		<thead>
			<tr>
				<th>Heading 7</th>
				<th>Heading 8</th>
				<th>Heading 9</th>
				<th>Heading 10</th>
				<th>Heading 11</th>
				<th>Heading 12</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>Data row 1 col 1</td>
				<td>Data row 1 col 2</td>
				<td>Data row 1 col 3</td>
				<td>Data row 1 col 4</td>
				<td>Data row 1 col 5</td>
				<td>Data row 1 col 6</td>
			</tr>
			<tr>
				<td>Data row 2 col 1</td>
				<td>Data row 2 col 2</td>
				<td>Data row 2 col 3</td>
				<td>Data row 2 col 4</td>
				<td>Data row 2 col 5</td>
				<td>Data row 2 col 6</td>
			</tr>
			<tr>
				<td>Data row 3 col 1</td>
				<td>Data row 3 col 2</td>
				<td>Data row 3 col 3</td>
				<td>Data row 3 col 4</td>
				<td>Data row 3 col 5</td>
				<td>Data row 3 col 6</td>
			</tr>
			<tr>
				<td>Data row 4 col 1</td>
				<td>Data row 4 col 2</td>
				<td>Data row 4 col 3</td>
				<td>Data row 4 col 4</td>
				<td>Data row 4 col 5</td>
				<td>Data row 4 col 6</td>
			</tr>
			<tr>
				<td>Data row 5 col 1</td>
				<td>Data row 5 col 2</td>
				<td>Data row 5 col 3</td>
				<td>Data row 5 col 4</td>
				<td>Data row 5 col 5</td>
				<td>Data row 5 col 6</td>
			</tr>
			<tr>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
			</tr>
		</tbody>
	</table>

	</body>
</html>

Strona zawiera 2 tabele i 6 kolumn, z których każda ma unikalne nazwy kolumn i 6 wierszy ze zmiennymi danymi. Ostatni wiersz zawiera Modifyprzycisk w obu tabelach.

Zakładając, że użytkownik musi wybrać czwarty Modifyprzycisk z pierwszej tabeli na podstawie nagłówka

Użyj xpath //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button

count()Operator jest przydatna w takich sytuacjach.

Logika:

  1. Znajdź nagłówek Modifyprzycisku za pomocą//th[.='Heading 4']
  2. Znajdź indeks kolumny nagłówka za pomocą count(//tr/th[.='Heading 4']/preceding-sibling::th)+1

Uwaga: Indeks zaczyna się od0

  1. Pobierz wiersze dla odpowiedniego nagłówka za pomocą //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]

  2. Pobierz Modifyprzycisk z wyodrębnionej listy węzłów za pomocą//th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button

Eric Stanley
źródło
wielkie dzięki! używam tego, ale doszedłem do td[count(../preceding-sibling::tr/th[.='Heading 4'])]/following-sibling::tdtego, że nie wpadam za td[1]każdym razem, gdy nie mam jakiegoś nagłówka :)
mani
2
//img[@title='Modify'][i]

jest skrótem od

/descendant-or-self::node()/img[@title='Modify'][i]

stąd zwraca i-ty węzeł pod tym samym węzłem macierzystym.

Chcesz

/descendant-or-self::img[@title='Modify'][i]
Nick Jones
źródło
1
Po prostu /descendant::img[@title='Modify'][$index]będzie działać dobrze. Zwróć również uwagę, że [i]predykat sprawdza istnienie ielementu potomnego.
1

(// * [@ atrybut = 'wartość']) [indeks], aby znaleźć cel elementu podczas znajdowania w nim wielu dopasowań

mahesh
źródło
1
Czy możesz wyjaśnić trochę więcej?
abhiarora
0

Oto rozwiązanie dla zmiennej indeksu

Powiedzmy, że znalazłeś 5 elementów z tym samym lokalizatorem i chciałbyś wykonać akcję na każdym elemencie, podając numer indeksu (tutaj zmienna jest używana jako indeks jako „i”)

for(int i=1; i<=5; i++)
{
    string xPathWithVariable = "(//div[@class='className'])" + "[" + i + "]";
    driver.FindElement(By.XPath(xPathWithVariable)).Click();
}

Potrzeba XPath:

(//div[@class='className'])[1]
(//div[@class='className'])[2]
(//div[@class='className'])[3]
(//div[@class='className'])[4]
(//div[@class='className'])[5]
Srinivas Kassa
źródło