Bagaimana saya bisa mencocokkan pada atribut yang berisi string tertentu?

442

Saya mengalami masalah dalam memilih node berdasarkan atribut ketika atribut mengandung lebih dari satu kata. Sebagai contoh:

<div class="atag btag" />

Ini adalah ekspresi xpath saya:

//*[@class='atag']

Ekspresi bekerja dengan

<div class="atag" />

tetapi tidak untuk contoh sebelumnya. Bagaimana saya bisa memilih <div>?

orang gila
sumber
9
Pantas untuk menunjukkan, saya pikir, bahwa "atag btag" adalah atribut tunggal, bukan dua. Anda mencoba melakukan pencocokan substring di xpath.
skaffman
3
Ya Anda benar - itulah yang saya inginkan.
crazyrails
1
Inilah sebabnya mengapa Anda harus menggunakan pemilih CSS ... div.atagatau div.btag. Super sederhana, bukan pencocokan string, dan WAY lebih cepat (dan lebih didukung di browser). XPath (terhadap HTML) harus diturunkan ke apa yang berguna untuk ... menemukan elemen dengan teks yang terkandung dan untuk navigasi DOM.
JeffC

Jawaban:

486

Berikut ini adalah contoh yang menemukan elemen div yang className-nya berisi atag:

//div[contains(@class, 'atag')]

Berikut ini contoh yang menemukan elemen div yang className-nya berisi atagdan btag:

//div[contains(@class, 'atag') and contains(@class ,'btag')]

Namun, ia juga akan menemukan kecocokan sebagian class="catag bobtag".

Jika Anda tidak ingin kecocokan sebagian, lihat jawaban bobince di bawah ini.

surupa123
sumber
123
@Redbeard: Ini adalah jawaban literal tetapi tidak biasanya tujuan solusi pencocokan kelas seharusnya. Khususnya itu akan cocok <div class="Patagonia Halbtagsarbeit">, yang berisi string target tetapi bukan div dengan kelas yang diberikan.
bobince
3
Ini akan berfungsi untuk skenario sederhana - tetapi hati-hati jika Anda ingin menggunakan jawaban ini dalam konteks yang lebih luas dengan sedikit atau tanpa kendali atas nilai atribut yang Anda periksa. Jawaban yang benar adalah milik bobince.
Oliver
16
Maaf, ini tidak cocok dengan kelas, ini cocok dengan substring
Timo Huovinen
5
itu jelas salah karena ditemukan juga: <div class = "annatag bobtag"> yang seharusnya tidak.
Alexei Vinogradov
6
Pertanyaannya adalah "mengandung string tertentu" tidak "cocok dengan kelas tertentu"
Alsatian
303

Jawaban mjv adalah awal yang baik tetapi akan gagal jika atag bukan nama kelas pertama yang terdaftar.

Pendekatan yang biasa adalah yang agak sulit:

//*[contains(concat(' ', @class, ' '), ' atag ')]

ini berfungsi selama kelas dipisahkan oleh spasi saja, dan bukan bentuk spasi putih lainnya. Ini hampir selalu demikian. Jika tidak, Anda harus membuatnya tetap lebih berat:

//*[contains(concat(' ', normalize-space(@class), ' '), ' atag ')]

(Memilih dengan string yang dipisahkan oleh ruang seperti classname adalah kasus umum sehingga mengejutkan bahwa tidak ada fungsi XPath khusus untuk itu, seperti '[class ~ = "atag"]' CSS3.)

bobince
sumber
57
bah, xpath perlu beberapa perbaikan
Randy L
13
@Redbeard jawaban supra123 bermasalah jika ada kelas css seperti "atagnumbertwo" yang tidak ingin Anda pilih, meskipun saya akui ini mungkin tidak mungkin (:
drevicko
7
@crazyrails: Bisakah Anda menerima jawaban ini sebagai jawaban yang benar? Itu akan membantu pencari di masa depan mengidentifikasi solusi yang tepat untuk masalah yang dijelaskan oleh pertanyaan Anda. Terima kasih!
Oliver
2
@ cha0site: Ya mereka bisa, di XPath 2.0 dan mengikuti. Jawaban ini ditulis sebelum XPath 2.0 menjadi resmi. Lihat stackoverflow.com/a/12165032/423105 atau stackoverflow.com/a/12165195/423105
LarsH
1
Jangan seperti saya dan hapus spasi di sekitar kelas yang Anda cari dalam contoh ini; mereka sebenarnya penting. Kalau tidak, itu mungkin terlihat berhasil tetapi mengalahkan tujuannya.
CTS_AE
40

coba ini: //*[contains(@class, 'atag')]

SelenUser
sumber
Bagaimana jika nama kelasnya grabatagonabag? (Petunjuk: masih akan cocok.)
Wayne
38

EDIT : lihat solusi bobince yang menggunakan berisi bukan mulai-dengan , bersama dengan trik untuk memastikan perbandingan dilakukan pada tingkat token lengkap (jangan sampai pola 'atag' ditemukan sebagai bagian dari 'tag' lain).

"atag btag" adalah nilai ganjil untuk atribut kelas, tetapi jangan pernah kurang, coba:

//*[starts-with(@class,"atag")]
mjv
sumber
Anda dapat menggunakan ini jika mesin XPath Anda mendukung perintah begin-with, misalnya JVM 6 tidak mendukungnya sejauh yang saya ingat
Mohamed Faramawi
10
@ mjv: Adalah umum untuk atribut kelas CSS untuk menentukan beberapa nilai. Begitulah cara CSS dilakukan.
skaffman
7
@ mjv Anda tidak dapat menjamin bahwa nama itu akan muncul di awal atribut kelas.
Alan Krueger
@ thuktun @skaffman. Terima kasih, komentar yang bagus. Saya 'diarahkan' ke solusi bobince sesuai.
mjv
Tidak bekerja untuk <div class = "btag atag"> yang setara dengan di atas
Alexei Vinogradov
30

XPath 2.0 yang berfungsi:

//*[tokenize(@class,'\s+')='atag']

atau dengan variabel:

//*[tokenize(@class,'\s+')=$classname]
Daniel Haley
sumber
Bagaimana ini bisa berfungsi jika @classmemiliki lebih dari satu elemen? Karena itu akan mengembalikan daftar kata dan membandingkannya dengan string gagal dengan kardinalitas yang salah .
Alexis Wilke
3
@AlexisWilke - Dari spec ( w3.org/TR/xpath20/#id-general-comparisons ): Perbandingan umum adalah perbandingan terukur secara eksistensial yang dapat diterapkan pada urutan operan dengan panjang berapa pun. Ini berfungsi di setiap prosesor 2.0 yang saya coba.
Daniel Haley
1
Perhatikan juga, dalam XPath 3.1 ini dapat disederhanakan menjadi//*[tokenize(@class)=$classname]
Michael Kay
1
Dan untuk kelengkapannya, jika Anda cukup beruntung untuk menggunakan prosesor XPath yang sadar skema, dan jika @class memiliki tipe bernilai daftar, maka Anda dapat menulis//*[@class=$classname]
Michael Kay
21

Ketahuilah bahwa jawaban bobince mungkin terlalu rumit jika Anda dapat menganggap bahwa nama kelas yang Anda minati bukanlah substring dari nama kelas lain yang mungkin . Jika ini benar, Anda bisa menggunakan pencocokan substring melalui fungsi berisi. Berikut ini akan cocok dengan elemen apa pun yang kelasnya mengandung substring 'atag':

//*[contains(@class,'atag')]

Jika asumsi di atas tidak berlaku, kecocokan substring akan cocok dengan elemen yang tidak Anda inginkan. Dalam hal ini, Anda harus menemukan batas kata. Dengan menggunakan pembatas ruang untuk menemukan batas nama kelas, jawaban kedua bobince menemukan kecocokan tepat:

//*[contains(concat(' ', normalize-space(@class), ' '), ' atag ')]

Ini akan cocok atagdan tidak matag.

Brent Atkinson
sumber
Ini adalah solusi yang saya cari. Itu jelas menemukan 'test' di class = 'hello test world' dan tidak cocok dengan 'hello test-test world'. Karena saya hanya menggunakan XPath 1.0 dan tidak memiliki RegEx ini hanya solusi yang berfungsi.
Jan Stanicek
7

Untuk menambahkan jawaban bobince ... Jika alat / pustaka apa pun yang Anda gunakan menggunakan Xpath 2.0, Anda juga dapat melakukan ini:

//*[count(index-of(tokenize(@class, '\s+' ), $classname)) = 1]

count () tampaknya diperlukan karena indeks-of () mengembalikan urutan setiap indeks yang cocok di dalam string.

tombol tentara
sumber
1
Saya kira Anda dimaksudkan untuk TIDAK menempatkan $classnamevariabel di antara tanda kutip? Karena sebagaimana adanya, itu adalah sebuah string.
Alexis Wilke
1
Akhirnya, implementasi getElementsByClassName yang benar (kompatibel dengan JavasScript) ... selain dari string literal '$classname'tentunya.
Joel Mellon
1
Ini terlalu rumit. Lihat jawaban @ DanielHaley untuk jawaban XPath 2.0 yang benar.
Michael Kay
5

Anda dapat mencoba yang berikut ini

By.CssSelector("div.atag.btag")

Umesh Chhabra
sumber
0

Saya datang ke sini mencari solusi untuk Ranorex Studio 9.0.1. Belum ada mengandung () di sana. Sebaliknya kita bisa menggunakan regex seperti:

div[@class~'atag']
Jarno Argillander
sumber
-1

Untuk tautan yang berisi url umum harus konsol dalam variabel. Kemudian cobalah secara berurutan.

webelements allLinks=driver.findelements(By.xpath("//a[contains(@href,'http://122.11.38.214/dl/appdl/application/apk')]"));
int linkCount=allLinks.length();
for(int i=0; <linkCount;i++)
{
    driver.findelement(allLinks[i]).click();
}
pengguna3906232
sumber