ฉันจะจับคู่กับแอตทริบิวต์ที่มีสตริงที่แน่นอนได้อย่างไร


442

ฉันมีปัญหาในการเลือกโหนดตามคุณลักษณะเมื่อแอตทริบิวต์มีมากกว่าหนึ่งคำ ตัวอย่างเช่น:

<div class="atag btag" />

นี่คือนิพจน์ xpath ของฉัน:

//*[@class='atag']

นิพจน์ใช้งานได้กับ

<div class="atag" />

แต่ไม่ใช่สำหรับตัวอย่างก่อนหน้า ฉันจะเลือก<div>?


9
ฉันคิดว่ามันคุ้มค่าที่จะบอกว่า "atag btag" เป็นคุณลักษณะเดียวไม่ใช่สอง คุณกำลังพยายามทำการจับคู่สตริงย่อยใน xpath
skaffman

3
ใช่คุณพูดถูก - นั่นคือสิ่งที่ฉันต้องการ
crazyrails


1
นี่คือเหตุผลที่คุณควรใช้ตัวเลือก CSS ... หรือdiv.atag div.btagง่ายสุดไม่ใช่การจับคู่สตริงและเร็วกว่า (และรองรับเบราว์เซอร์ได้ดีขึ้น) XPath (เทียบกับ HTML) ควรได้รับการจัดวางในสิ่งที่มีประโยชน์สำหรับ ... การค้นหาองค์ประกอบโดยใช้ข้อความที่มีอยู่และสำหรับการนำทาง DOM
JeffC

คำตอบ:


486

นี่คือตัวอย่างที่พบองค์ประกอบ div ที่ className ประกอบด้วยatag:

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

นี่คือตัวอย่างที่พบองค์ประกอบ div ที่ className ประกอบด้วยatagและbtag:

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

class="catag bobtag"แต่ก็ยังจะได้พบกับการแข่งขันบางส่วนเช่น

หากคุณไม่ต้องการการแข่งขันบางส่วนดูคำตอบของ bobince ด้านล่าง


123
@ Redbeard: มันเป็นคำตอบที่แท้จริง แต่โดยทั่วไปแล้วไม่ใช่สิ่งที่โซลูชันการจับคู่คลาสควรมีจุดมุ่งหมาย โดยเฉพาะอย่างยิ่งมันจะจับคู่<div class="Patagonia Halbtagsarbeit">ซึ่งมีสตริงเป้าหมาย แต่ไม่ใช่ div ที่มีคลาสที่กำหนด
bobince

3
สิ่งนี้จะใช้ได้กับสถานการณ์ง่าย ๆ แต่ระวังถ้าคุณต้องการใช้คำตอบนี้ในบริบทที่กว้างขึ้นโดยไม่ต้องควบคุมค่าคุณลักษณะที่คุณกำลังตรวจสอบอยู่ คำตอบที่ถูกต้องคือของ bobince
โอลิเวอร์

16
ขออภัยนี่ไม่ตรงกับคลาส แต่ตรงกับสตริงย่อย
Timo Huovinen

5
มันผิดอย่างชัดเจนเมื่อพบเช่น: <div class = "annatag bobtag"> ซึ่งไม่ควรใช้
Alexei Vinogradov

6
คำถามก็คือ "มีสตริงบางอย่าง" ไม่ได้ "ตรงกับชั้นหนึ่ง"
อัลเซเชี่ยน

303

คำตอบของ mjv เป็นการเริ่มต้นที่ดี แต่จะล้มเหลวหาก atag ไม่ใช่ classname แรกที่แสดงรายการ

วิธีการปกติคือค่อนข้างเทอะทะ:

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

ทำงานได้ตราบใดที่คลาสคั่นด้วยช่องว่างเท่านั้นและไม่ใช่ช่องว่างในรูปแบบอื่น นี่เป็นกรณีที่เกิดขึ้นเกือบตลอดเวลา หากไม่เป็นเช่นนั้นคุณจะต้องทำให้นิ่งเฉยยิ่งขึ้น:

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

(การเลือกโดยสตริงที่คั่นด้วยช่องว่างเหมือนชื่อคลาสเป็นกรณีทั่วไปมันน่าแปลกใจที่ไม่มีฟังก์ชัน XPath เฉพาะสำหรับมันเช่น CSS ['class ~ = "atag"]' ของ CSS3)


58
บา, xpath ต้องการการแก้ไขบางอย่าง
Randy L

13
@Redbeard supra123 คำตอบมีปัญหาหากมีคลาส css เช่น "atagnumbertwo" ที่คุณไม่ต้องการเลือกแม้ว่าฉันจะยอมรับว่านี่อาจไม่น่าจะเป็นไปได้ (:
drevicko

7
@crazyrails: คุณช่วยตอบคำตอบนี้ให้ถูกต้องได้ไหม? ซึ่งจะช่วยให้ผู้ค้นหาในอนาคตระบุวิธีแก้ไขปัญหาที่ถูกต้องตามคำถามของคุณ ขอบคุณ!
โอลิเวอร์

2
@ cha0site: ใช่พวกเขาทำได้ใน XPath 2.0 และต่อไปนี้ คำตอบนี้ถูกเขียนก่อนที่ XPath 2.0 จะเป็นทางการ ดูstackoverflow.com/a/12165032/423105หรือstackoverflow.com/a/12165195/423105
LarsH

1
อย่าเป็นเหมือนฉันและลบช่องว่างรอบ ๆ ชั้นเรียนที่คุณต้องการในตัวอย่างนี้ พวกมันสำคัญจริง ๆ ไม่อย่างนั้นมันอาจจะดูเหมือนว่าจะทำงานได้ แต่เอาชนะจุดประสงค์
CTS_AE

40

ลองนี้: //*[contains(@class, 'atag')]


เกิดอะไรขึ้นถ้าชื่อคลาสคือgrabatagonabagอะไร? (คำแนะนำ: มันจะยังคงตรงกัน)
Wayne

38

แก้ไข : ดูโซลูชันของ bobince ที่ใช้มีมากกว่าเริ่มต้นพร้อมกับเคล็ดลับเพื่อให้แน่ใจว่าการเปรียบเทียบเสร็จสิ้นในระดับโทเค็นที่สมบูรณ์

"atag btag" เป็นค่าคี่สำหรับแอตทริบิวต์ class แต่อย่าน้อยลอง:

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

คุณสามารถใช้สิ่งนี้หากเอ็นจิ้น XPath ของคุณรองรับคำสั่งเริ่มต้นด้วยตัวอย่างเช่น JVM 6 ไม่สนับสนุนเท่าที่ฉันจำได้
Mohamed Faramawi

10
@mjv: เป็นเรื่องปกติสำหรับแอตทริบิวต์คลาส CSS เพื่อระบุหลายค่า นั่นเป็นวิธีที่ CSS ทำ
skaffman

7
@mjv คุณไม่สามารถรับประกันได้ว่าชื่อนั้นจะปรากฏที่จุดเริ่มต้นของแอตทริบิวต์ class
อลันครูเกอร์

@thuktun @skaffman ขอบคุณความคิดเห็นที่ดี ฉัน 'เปลี่ยนเส้นทาง' เพื่อแก้ไขปัญหาตามลำดับ
mjv

ใช้งานไม่ได้กับ <div class = "btag atag"> ซึ่งเทียบเท่ากับข้างต้น
Alexei Vinogradov

30

2.0 XPath ที่ใช้งานได้:

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

หรือด้วยตัวแปร:

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

มันจะทำงาน@classได้อย่างไรถ้ามีองค์ประกอบมากกว่าหนึ่ง เพราะมันจะส่งคืนรายการคำและเปรียบเทียบกับสตริงที่ล้มเหลวด้วยความผิดปกติเชิงหัวใจ
Alexis Wilke

3
@AlexisWilke - จากข้อมูลจำเพาะ ( w3.org/TR/xpath20/#id-general-comparisons ): การเปรียบเทียบทั่วไปเป็นการเปรียบเทียบเชิงปริมาณที่มีอยู่ที่อาจนำไปใช้กับลำดับตัวถูกดำเนินการของความยาวใด ๆ ใช้งานได้กับโปรเซสเซอร์ 2.0 ทุกตัวที่ฉันได้ลอง
Daniel Haley

1
หมายเหตุด้วยใน XPath 3.1 สามารถทำให้ง่ายขึ้น//*[tokenize(@class)=$classname]
Michael Kay

1
และเพื่อความสมบูรณ์ถ้าคุณโชคดีพอที่จะใช้ตัวประมวลผล XPath สกีมาที่รับรู้และหาก @class มีประเภทรายการที่มีมูลค่าคุณสามารถเขียนได้อย่างง่ายดาย//*[@class=$classname]
Michael Kay

21

โปรดทราบว่าคำตอบ bobince อาจจะมีความซับซ้อนมากเกินไปถ้าคุณสามารถสมมติว่าชื่อชั้นที่คุณกำลังสนใจในการไม่ได้เป็นย่อยของชื่อชั้นเป็นไปได้อีก หากเป็นจริงคุณสามารถใช้การจับคู่สตริงย่อยผ่านฟังก์ชันมี ต่อไปนี้จะจับคู่องค์ประกอบใด ๆ ที่คลาสมีสตริงย่อย 'atag':

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

หากข้อสันนิษฐานข้างต้นไม่อยู่การจับคู่สตริงย่อยจะจับคู่องค์ประกอบที่คุณไม่ต้องการ ในกรณีนี้คุณต้องค้นหาขอบเขตของคำ ด้วยการใช้ตัวคั่นช่องว่างเพื่อค้นหาขอบเขตชื่อคลาสคำตอบที่สองของ bobince จะค้นหาการจับคู่ที่ตรงกัน:

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

นี้จะตรงและไม่ได้atagmatag


นี่คือทางออกที่ฉันกำลังมองหา พบอย่างชัดเจนว่า 'ทดสอบ' ใน class = 'hello test world' และไม่ตรงกับ 'hello test-test world' เนื่องจากฉันใช้เฉพาะ XPath 1.0 และไม่มี RegEx นี่เป็นเพียงโซลูชันที่ใช้งานได้
Jan Stanicek

7

หากต้องการเพิ่มคำตอบของ bobince ... หากเครื่องมือ / ไลบรารีใด ๆ ที่คุณใช้ใช้ Xpath 2.0 คุณสามารถทำสิ่งนี้ได้:

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

มีความจำเป็นที่จะต้องนับ () เนื่องจาก index-of () ส่งคืนลำดับของแต่ละดัชนีที่มีการจับคู่ที่ในสตริง


1
ฉันคิดว่าคุณหมายถึงไม่ใส่$classnameตัวแปรระหว่างเครื่องหมายคำพูด? เพราะอย่างที่เป็นนั่นคือสตริง
Alexis Wilke

1
สุดท้ายการนำไปใช้งานที่ถูกต้อง (เข้ากันได้กับ JavasScript) ของ getElementsByClassName ... นอกเหนือจากตัวอักษรสตริง'$classname'แน่นอน
Joel Mellon

1
นี่มันซับซ้อนเกินความคาดหมาย ดูการตอบสนองของ @ DanielHaley สำหรับคำตอบ XPath 2.0 ที่ถูกต้อง
Michael Kay


0

ฉันมาที่นี่เพื่อค้นหาวิธีแก้ปัญหาสำหรับ Ranorex Studio 9.0.1 ยังไม่มีการบรรจุ () ที่นั่น แต่เราสามารถใช้ regex เช่น:

div[@class~'atag']

-1

สำหรับลิงค์ที่มี URL ทั่วไปจะต้องมีคอนโซลในตัวแปร จากนั้นลองตามลำดับ

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();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.