Làm thế nào tôi có thể khớp với một thuộc tính có chứa một chuỗi nhất định?


442

Tôi gặp vấn đề khi chọn các nút theo thuộc tính khi các thuộc tính chứa nhiều hơn một từ. Ví dụ:

<div class="atag btag" />

Đây là biểu thức xpath của tôi:

//*[@class='atag']

Biểu thức làm việc với

<div class="atag" />

nhưng không cho ví dụ trước. Làm thế nào tôi có thể chọn <div>?


9
Thật đáng để chỉ ra, tôi nghĩ rằng "atag btag" là một thuộc tính duy nhất, không phải hai. Bạn đang cố gắng thực hiện khớp chuỗi con trong xpath.
skaffman

3
Có bạn đúng - đó là những gì tôi muốn.
crazyrails


1
Đây là lý do tại sao bạn nên sử dụng bộ chọn CSS ... div.ataghoặc div.btag. Siêu đơn giản, không khớp chuỗi và CÁCH nhanh hơn (và được hỗ trợ tốt hơn trong trình duyệt). XPath (so với HTML) nên được chuyển thành những gì hữu ích cho việc ... tìm các phần tử bằng văn bản chứa và điều hướng DOM.
JeffC

Câu trả lời:


486

Dưới đây là một ví dụ tìm thấy các phần tử div có className chứa atag:

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

Dưới đây là một ví dụ tìm thấy các phần tử div có className chứa atagbtag:

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

Tuy nhiên, nó cũng sẽ tìm thấy một phần phù hợp như thế nào class="catag bobtag".

Nếu bạn không muốn khớp một phần, hãy xem câu trả lời của bobince bên dưới.


123
@Redbeard: Đó là một câu trả lời theo nghĩa đen nhưng thường không phải là giải pháp phù hợp với lớp học. Cụ thể, nó sẽ khớp <div class="Patagonia Halbtagsarbeit">, chứa chuỗi đích nhưng không phải là div với các lớp đã cho.
bobince

3
Điều này sẽ hoạt động cho các kịch bản đơn giản - nhưng xem ra nếu bạn muốn sử dụng câu trả lời này trong bối cảnh rộng hơn với ít hoặc không kiểm soát các giá trị thuộc tính bạn đang kiểm tra. Câu trả lời đúng là của bobince.
Oliver

16
Xin lỗi, điều này không phù hợp với một lớp, nó phù hợp với một chuỗi con
Timo Huovinen 20/03/2016

5
nó hoàn toàn sai khi nó cũng tìm thấy: <div class = "annatag bussyag"> mà nó không nên.
Alexei Vinogradov

6
Câu hỏi là "chứa một chuỗi nhất định" không "khớp với một lớp nhất định"
Alsatian

303

Câu trả lời của mjv là một khởi đầu tốt nhưng sẽ thất bại nếu atag không phải là tên lớp đầu tiên được liệt kê.

Cách tiếp cận thông thường là khá khó sử dụng:

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

điều này hoạt động miễn là các lớp chỉ được phân tách bằng dấu cách và không phải là các dạng khoảng trắng khác. Điều này gần như luôn luôn là trường hợp. Nếu có thể không, bạn phải làm cho nó khó sử dụng hơn:

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

(Việc chọn theo các chuỗi phân tách không gian giống như tên lớp là một trường hợp phổ biến, thật đáng ngạc nhiên khi không có chức năng XPath cụ thể cho nó, như '[class ~ = "atag"] của CSS3.)


57
bah, xpath cần một số bản sửa lỗi
Randy L

13
Câu trả lời của @Redbeard supra123 có vấn đề nếu có một lớp css như "atagnumbertwo" mà bạn không muốn chọn, mặc dù tôi sẽ thừa nhận điều này có thể không xảy ra (:
drevicko

7
@crazyrails: Bạn có thể vui lòng chấp nhận câu trả lời này là câu trả lời đúng không? Điều đó sẽ giúp người tìm kiếm trong tương lai xác định giải pháp chính xác cho vấn đề được mô tả bởi câu hỏi của bạn. Cảm ơn bạn!
Oliver

2
@ cha0site: Có, họ có thể, trong XPath 2.0 và tiếp theo. Câu trả lời này được viết trước khi XPath 2.0 trở thành chính thức. Xem stackoverflow.com/a/12165032/423105 hoặc stackoverflow.com/a/12165195/423105
LarsH

1
Đừng như tôi và xóa các khoảng trống xung quanh lớp bạn đang tìm kiếm trong ví dụ này; chúng thực sự quan trọng. Nếu không, nó có thể tìm cách làm việc nhưng đánh bại mục đích.
CTS_AE


38

EDIT : xem giải pháp của bobince sử dụng chứa chứ không phải bắt đầu , cùng với một mẹo để đảm bảo so sánh được thực hiện ở cấp độ của một mã thông báo hoàn chỉnh (kẻo sẽ tìm thấy mẫu 'atag' như một phần của 'thẻ' khác).

"atag btag" là một giá trị lẻ cho thuộc tính lớp, nhưng không bao giờ là ít hơn, hãy thử:

//*[starts-with(@class,"atag")]

bạn có thể sử dụng điều này nếu công cụ XPath của bạn hỗ trợ lệnh start-with, ví dụ JVM 6 không hỗ trợ nó theo như tôi nhớ
Mohamed Faramawi

10
@mjv: Nó phổ biến cho một thuộc tính lớp CSS để chỉ định nhiều giá trị. Đó là cách CSS được thực hiện.
skaffman

7
@mjv Bạn không thể đảm bảo rằng tên đó sẽ xuất hiện ở đầu thuộc tính lớp.
Alan Krueger

@thuktun @skaffman. Cảm ơn, ý kiến ​​tuyệt vời. Tôi 'chuyển hướng' để giải pháp bobince phù hợp.
mjv

Không hoạt động cho <div class = "btag atag"> tương đương với ở trên
Alexei Vinogradov

30

XPath 2.0 hoạt động:

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

hoặc với một biến:

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

Làm thế nào điều này có thể làm việc nếu @classcó nhiều hơn một yếu tố? Bởi vì nó sẽ trả về một danh sách các từ và so sánh điều đó với một chuỗi thất bại với cardinality sai .
Alexis Wilke

3
@AlexisWilke - Từ thông số kỹ thuật ( w3.org/TR/xpath20/#id-general-comparisons ): So sánh chung là so sánh định lượng tồn tại có thể được áp dụng cho các chuỗi toán hạng có độ dài bất kỳ. Nó hoạt động trong mọi bộ xử lý 2.0 mà tôi đã thử.
Daniel Haley

1
Cũng lưu ý, trong XPath 3.1, điều này có thể được đơn giản hóa thành//*[tokenize(@class)=$classname]
Michael Kay

1
Và để hoàn thiện, nếu bạn may mắn được sử dụng bộ xử lý XPath nhận biết lược đồ và nếu @ class có loại có giá trị danh sách, thì bạn có thể chỉ cần viết//*[@class=$classname]
Michael Kay

21

Xin lưu ý rằng câu trả lời của bobince có thể quá phức tạp nếu bạn có thể cho rằng tên lớp bạn quan tâm không phải là một chuỗi con của tên lớp khác có thể . Nếu điều này là đúng, bạn chỉ cần sử dụng khớp chuỗi con thông qua hàm chứa. Phần sau đây sẽ khớp với bất kỳ phần tử nào có lớp chứa chuỗi con 'atag':

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

Nếu giả định ở trên không giữ, một kết hợp chuỗi con sẽ khớp với các yếu tố bạn không có ý định. Trong trường hợp này, bạn phải tìm ranh giới từ. Bằng cách sử dụng các dấu phân cách không gian để tìm ranh giới tên lớp, câu trả lời thứ hai của bobince tìm thấy các kết quả khớp chính xác:

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

Điều này sẽ phù hợp atagvà không matag.


Đây là giải pháp tôi đang tìm kiếm. Nó rõ ràng tìm thấy 'test' trong lớp = 'xin chào thế giới thử nghiệm' và không khớp với 'thế giới thử nghiệm xin chào'. Vì tôi chỉ sử dụng XPath 1.0 và không có RegEx nên đây chỉ là giải pháp hoạt động.
Jan Stanicek

7

Để thêm vào câu trả lời của bobince ... Nếu bất kỳ công cụ / thư viện nào bạn sử dụng sử dụng Xpath 2.0, bạn cũng có thể thực hiện việc này:

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

Count () rõ ràng là cần thiết bởi vì index-of () trả về một chuỗi của mỗi chỉ mục mà nó có khớp trong chuỗi.


1
Tôi cho rằng bạn có nghĩa là KHÔNG đặt $classnamebiến giữa các dấu ngoặc kép? Bởi vì như nó là, đó là một chuỗi.
Alexis Wilke

1
Cuối cùng, một triển khai chính xác (tương thích JavasScript) của getElementsByClassName ... ngoài chuỗi ký tự '$classname'tất nhiên.
Joel Mellon

1
Điều này là quá phức tạp. Xem phản hồi của @ DanielHaley để biết câu trả lời XPath 2.0 chính xác.
Michael Kay


0

Tôi đến đây tìm kiếm giải pháp cho Ranorex Studio 9.0.1. Không có chứa () ở đó. Thay vào đó chúng ta có thể sử dụng regex như:

div[@class~'atag']

-1

Đối với các liên kết có chứa url chung phải điều khiển trong một biến. Sau đó thử nó tuần tự.

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();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.