ฉันจะค้นหาองค์ประกอบที่มีข้อความเฉพาะใน Selenium Webdriver (Python) ได้อย่างไร


259

ฉันพยายามทดสอบอินเตอร์เฟส javascript ที่ซับซ้อนด้วย Selenium (ใช้ส่วนต่อประสาน Python และในเบราว์เซอร์หลายตัว) ฉันมีปุ่มหลายรูปแบบ:

<div>My Button</div>

ฉันต้องการค้นหาปุ่มตาม "ปุ่มของฉัน" (หรือไม่ตรงตามตัวพิมพ์ใหญ่ - เล็กและตรงเช่น "ปุ่มของฉัน" หรือ "ปุ่ม")

ฉันพบว่ามันยากอย่างน่าอัศจรรย์เท่าที่ฉันรู้สึกว่าฉันขาดอะไรบางอย่างที่เห็นได้ชัด สิ่งที่ดีที่สุดที่ฉันมีคือ:

driver.find_elements_by_xpath('//div[contains(text(), "' + text + '")]')

อย่างไรก็ตามแบบตรงตามตัวพิมพ์ใหญ่ - เล็ก สิ่งอื่น ๆ ที่ฉันได้ลองทำก็คือวนซ้ำทั้งหมดในหน้าและตรวจสอบคุณสมบัติ element.text อย่างไรก็ตามทุกครั้งที่คุณได้รับแบบฟอร์ม:

<div class="outer"><div class="inner">My Button</div></div>

div.outer ยังมี "ปุ่มของฉัน" เป็นข้อความ ในการแก้ไขปัญหานั้นฉันได้ลองดูเพื่อดูว่า div.outer เป็นพาเรนต์ของ div.inner แต่ไม่สามารถหาวิธีการทำเช่นนั้นได้ การทดสอบไม่เท่ากับ div.outer) นอกจากนี้การวนองค์ประกอบทั้งหมดในหน้านั้นดูเหมือนจะช้ามากอย่างน้อยก็ใช้ Chrome webdriver

ไอเดีย?

แก้ไข: คำถามนี้คลุมเครือเล็กน้อย ถาม (และตอบ) รุ่นที่เฉพาะเจาะจงมากขึ้นที่นี่: วิธีรับข้อความขององค์ประกอบใน Selenium WebDriver (ผ่าน Python API) โดยไม่รวมข้อความองค์ประกอบลูก


คำตอบปัจจุบันไม่ได้ผลสำหรับฉัน อันนี้ทำ: sqa.stackexchange.com/a/2486
alejandro

คำตอบ:


328

ลองทำสิ่งต่อไปนี้:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")

3
ขอบคุณสำหรับการตอบกลับเป็น 50% ของสิ่งที่ฉันต้องการ (ให้ฉันเริ่มต้น) แบบฟอร์มที่ฉันไปถึงคือ "(// * [ประกอบด้วย (ข้อความ (), '" + ข้อความ + "')] | // * [@ value = '" + ข้อความ + "'])" มันจะค้นหา ข้อความที่กำหนดไม่เพียง แต่ในโหนดองค์ประกอบเท่านั้น แต่ยังอยู่ในองค์ประกอบอินพุตที่ข้อความถูกตั้งค่าผ่านแอตทริบิวต์ 'value' เช่น <button value = "My Button" /> แม้ว่าจะทราบว่าค่าจะต้องตรงกับที่เข้มงวดไม่เพียง แต่มีข้อความ
Ivan Koshelev

9
ด้วยมูลค่าการกล่าวขวัญสำหรับผู้เข้าชมเครื่องมือค้นหาอื่น ๆ : หากคุณกำลังมองหาลิงค์มีfind_element(s)_by_link_textและfind_element(s)_by_partial_link_textวิธีการ
Dan Passaro

3
เกิดอะไรขึ้นถ้าข้อความเป็นแบบไดนามิก นั่นคืออาจมีคำพูด จะไม่ทำลายโซลูชันนี้หรือไม่
IcedDante

3
การค้นหาชื่อบางชื่อดูเหมือนจะทำให้เกิดความเสียหาย ทำสิ่งต่อไปนี้เป็นตัวอย่าง: "// * [ประกอบด้วย (ข้อความ (), '" + ชื่อผู้ใช้ + "')]" ถ้า username = "O'Reilly"; ดังนั้น xpath จะไม่ถูกต้อง มีวิธีแก้ไขไหม?
Sakamoto Kazuma

ดูเหมือนจะไม่ทำงานเมื่อข้อความเป้าหมายมีหลายบรรทัด
Shawn

29

คุณสามารถลอง xpath เช่น:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)

ขอบคุณ ... เพื่อที่จะช่วยแยกแยะด้านในจากด้านนอก แต่มันใช้งานได้ดีกับ xpath ฉันแค่มีปัญหานั้นวนซ้ำผ่าน div ทั้งหมด ปัญหาของฉันกับ xpath คือฉันไม่สามารถหาวิธีทำให้ตัวพิมพ์เล็กและตัวพิมพ์เล็กได้
Josh

2
xpath 2.0 มีฟังก์ชั่นตัวพิมพ์เล็กดังนั้นสิ่งนี้ควรใช้งานได้: '// div [ประกอบด้วย (ตัวพิมพ์เล็ก (ข้อความ ()), "{0}")]'. รูปแบบ (ข้อความ)
andrean

ขอบคุณ! แม้ว่าความเข้าใจของผมคือว่า XPath 2.0 ไม่สนับสนุนในเบราว์เซอร์ที่สำคัญ ...
Josh

ซีลีเนียมประเมิน xpath expressions โดยตรงกับวิธีการของเบราว์เซอร์ดังนั้นมันขึ้นอยู่กับว่าคุณใช้เบราว์เซอร์ใดกับซีลีเนียม โดยทั่วไปเท่านั้นเช่น 6,7 และ 8 ไม่ควรสนับสนุน xpath 2.0
andrean

.formatไม่รู้จักในคราสของฉัน มันให้และข้อผิดพลาด ความคิดใด ๆ ทำไม
anujin

16

คุณสามารถใช้กับรูปแบบวัตถุหน้าเช่น:

ลองรหัสนี้:

@FindBy(xpath = "//*[contains(text(), 'Best Choice')]")
WebElement buttonBestChoice;

13

// * จะมองหาแท็ก HTML ใด ๆ หากข้อความบางข้อความเป็นเรื่องธรรมดาสำหรับแท็กปุ่มและ div และหาก // // เป็นหมวดหมู่ข้อความนั้นจะไม่ทำงานอย่างที่คาดไว้ หากคุณต้องการเลือกเฉพาะคุณสามารถรับได้โดยประกาศแท็กองค์ประกอบ HTML ชอบ:

driver.find_element_by_xpath("//div[contains(text(),'Add User')]")
driver.find_element_by_xpath("//button[contains(text(),'Add User')]")

5

ที่น่าสนใจแทบทุกคำตอบหมุนรอบการทำงานของ XPath contains()ละเลยความเป็นจริงมันเป็นกรณีที่มีความสำคัญ - ตรงกันข้ามกับของ OP ถาม
หากคุณต้องการความรู้สึกตัวพิมพ์เล็กและตัวพิมพ์ใหญ่นั้นสามารถทำได้ใน xpath 1.0 (เวอร์ชันปัจจุบันสนับสนุนเบราว์เซอร์)แม้ว่ามันจะไม่สวย - โดยการใช้translate()ฟังก์ชั่น มันทดแทนตัวละครแหล่งที่มากับรูปแบบที่ต้องการโดยใช้ตารางการแปล

การสร้างตารางของอักขระตัวพิมพ์ใหญ่ทั้งหมดจะแปลงข้อความของโหนดให้เป็นแบบฟอร์มล่าง () ได้อย่างมีประสิทธิภาพช่วยให้การจับคู่แบบตัวพิมพ์เล็กและตัวพิมพ์เล็ก(นี่เป็นเพียงสิทธิพิเศษ) :

[
  contains(
    translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),
    'my button'
  )
]
# will match a source text like "mY bUTTon"

การเรียกหลามแบบเต็ม:

driver.find_elements_by_xpath("//*[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZЙ', 'abcdefghijklmnopqrstuvwxyzй'), 'my button')]")

โดยปกติวิธีการนี้จะมีข้อเสีย - ตามที่ระบุไว้มันจะใช้ได้กับข้อความภาษาละตินเท่านั้น หากคุณต้องการครอบคลุมอักขระยูนิโค้ด - คุณจะต้องเพิ่มลงในตารางการแปล ผมเคยทำว่าในตัวอย่างข้างต้น - "Й"ตัวอักษรตัวสุดท้ายคือสัญลักษณ์ริลลิก


และถ้าเราอาศัยอยู่ในโลกที่เบราว์เซอร์ที่สนับสนุน XPath 2.0 และค่า(🤞 แต่ไม่ได้เกิดขึ้นในเร็ว ๆ นี้☹️)เราจะได้มีการใช้ฟังก์ชั่นlower-case()(ยังไม่เต็มที่รู้จักสถานที่) และmatches(สำหรับการค้นหา regex กับกรณี - แฟล็ก -insensitive ( 'i')


3

ใน HTML ที่คุณให้ไว้:

<div>My Button</div>

ข้อความMy Buttonเป็นinnerHTMLและไม่มีช่องว่างล้อมรอบเพื่อให้คุณสามารถใช้งานได้ง่ายtext()ดังนี้:

my_element = driver.find_element_by_xpath("//div[text()='My Button']")

หมายเหตุ : text()เลือกชายด์โหนดข้อความทั้งหมดของโหนดบริบท


ข้อความที่มีช่องว่างนำหน้า / ต่อท้าย

ใส่ข้อความที่เกี่ยวข้องซึ่งมีช่องว่างไว้ในตอนต้น:

<div>   My Button</div>

หรือในตอนท้าย:

<div>My Button   </div>

หรือที่ปลายทั้งสอง:

<div> My Button </div>  

ในกรณีเหล่านี้คุณมี 2 ตัวเลือก:

  • คุณสามารถใช้contains()ฟังก์ชั่นที่กำหนดว่าสตริงอาร์กิวเมนต์แรกมีสตริงอาร์กิวเมนต์ที่สองและส่งกลับบูลีนจริงหรือเท็จดังนี้

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • คุณสามารถใช้normalize-space()ฟังก์ชั่นที่ตัดส่วนหัวและส่วนท้ายของช่องว่างจากสตริงแทนที่ลำดับของอักขระช่องว่างด้วยช่องว่างเดียวและส่งคืนสตริงผลลัพธ์ดังนี้

    driver.find_element_by_xpath("//div[normalize-space()='My Button']]")

xpath สำหรับตัวแปรข้อความ

ในกรณีที่ข้อความเป็นตัวแปรที่คุณสามารถใช้ได้:

foo= "foo_bar"
my_element = driver.find_element_by_xpath("//div[.='" + foo + "']")

1
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(text(), 'YourTextHere')]")));
    assertNotNull(driver.findElement(By.xpath("//*[contains(text(), 'YourTextHere')]")));
    String yourButtonName=driver.findElement(By.xpath("//*[contains(text(), 'YourTextHere')]")).getAttribute("innerText");
    assertTrue(yourButtonName.equalsIgnoreCase("YourTextHere"));

1

ปัญหาที่คล้ายกัน: ค้นหา <button>Advanced...</button>

บางทีนี่อาจจะทำให้คุณมีความคิด (กรุณาโอนแนวคิดจาก Java เป็น Python):

wait.until(ExpectedConditions.elementToBeClickable(//
    driver.findElements(By.tagName("button")).stream().filter(i -> i.getText().equals("Advanced...")).findFirst().get())).click();

0

ใช้driver.find_elements_by_xpathและตรงกับ regex ฟังก์ชั่นการจับคู่สำหรับกรณีการค้นหาตายขององค์ประกอบโดยข้อความ

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")

matches()เป็นฟังก์ชั่น xpath 2.0 และเบราว์เซอร์เสียใจที่รองรับเฉพาะ 1.0 เท่านั้น
Todor Minakov

-19

ลองสิ่งนี้ มันง่ายมาก:

driver.getPageSource().contains("text to search");

มันใช้งานได้จริงสำหรับฉันในไดรเวอร์เว็บซีลีเนียม


9
ไม่ทำงานหากข้อความถูกสร้างโดย JavaScript
palacsint

2
นี่เป็นวิธีที่ดีในการตรวจสอบเนื่องจากคุณกำลังถ่ายโอนเนื้อหาทั้งหมดของหน้าเว็บผ่านสาย สำหรับหน้าขนาดเล็กมากสิ่งนี้เป็นสิ่งที่ยอมรับได้ แต่สำหรับหน้าขนาดใหญ่มากคุณจะถ่ายโอนเนื้อหาทั้งหมดของไฟล์และตรวจสอบทางฝั่งเซิร์ฟเวอร์ วิธีที่ดีกว่าคือการทำในฝั่งไคลเอ็นต์ด้วย xpath, javascript หรือ css
thomas.han

ฉันคิดว่าแหล่งที่มาของหน้าทั้งหมดจะต้องมีการโอนผ่านสายเพื่อให้เบราว์เซอร์แสดงผลหรือไม่
René

3
Josh กำลังถามวิธีการค้นหาองค์ประกอบด้วยข้อความไม่ใช่เพื่อทดสอบว่ามีข้อความอยู่ในแหล่งที่มาของหน้าเว็บหรือไม่
เซดริก

1
สำหรับอินสแตนซ์ที่จำเป็นทั้งหมดคือค้นหาข้อความคงที่บนหน้าโซลูชันนี้ดีพอ (มันช่วยในกรณีของฉัน)
Karlth
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.