วิธีคลิกองค์ประกอบที่มีข้อความใน Puppeteer


91

มีวิธีใดบ้าง (ไม่พบใน API) หรือวิธีการคลิกองค์ประกอบที่มีข้อความ

ตัวอย่างเช่นฉันมี html:

<div class="elements">
    <button>Button text</button>
    <a href=#>Href text</a>
    <div>Div text</div>
</div>

และฉันต้องการคลิกที่องค์ประกอบที่มีการรวมข้อความไว้ (คลิกที่ปุ่มภายใน. องค์ประกอบ) เช่น:

Page.click('Button text', '.elements')

2
แชร์คำตอบที่นี่: stackoverflow.com/a/47829000/6161265
Md. Abu Taher

คุณพบคำตอบหรือไม่?
Shubham Batra

หากช่วยได้หน้าเว็บที่ฉันต้องการคลิกมี jQuery โหลดดังนั้นฉันจึงสามารถ & ใช้วิธีการประเมินเพื่อรันโค้ด jQuery
Brandito

คำตอบ:


84

คำตอบสั้น ๆ

นิพจน์ XPath นี้จะค้นหาปุ่มที่มีข้อความ "Button text":

const [button] = await page.$x("//button[contains(., 'Button text')]");
if (button) {
    await button.click();
}

หากต้องการเคารพ<div class="elements">ปุ่มโดยรอบให้ใช้รหัสต่อไปนี้:

const [button] = await page.$x("//div[@class='elements']/button[contains(., 'Button text')]");

คำอธิบาย

เพื่ออธิบายว่าเหตุใดการใช้โหนดข้อความ ( text()) จึงผิดในบางกรณีลองดูตัวอย่าง:

<div>
    <button>Start End</button>
    <button>Start <em>Middle</em> End</button>
</div>

ขั้นแรกให้ตรวจสอบผลลัพธ์เมื่อใช้contains(text(), 'Text'):

  • //button[contains(text(), 'Start')]จะส่งคืนทั้งสองโหนด (ตามที่คาดไว้)
  • //button[contains(text(), 'End')]จะคืนหนึ่งโหนด (ครั้งแรก) ขณะที่text()ผลตอบแทนรายการที่มีทั้งสองข้อความ (กStart และ End) แต่containsจะตรวจสอบเฉพาะคนแรก
  • //button[contains(text(), 'Middle')] จะไม่ส่งคืนผลลัพธ์เนื่องจากtext()ไม่มีข้อความของโหนดลูก

นี่คือนิพจน์ XPath สำหรับcontains(., 'Text')ซึ่งทำงานกับองค์ประกอบเองรวมถึงโหนดลูกด้วย:

  • //button[contains(., 'Start')]จะส่งคืนทั้งสองปุ่ม
  • //button[contains(., 'End')]จะส่งคืนทั้งสองปุ่มอีกครั้ง
  • //button[contains(., 'Middle')] จะส่งคืนหนึ่ง (ปุ่มสุดท้าย)

ดังนั้นในกรณีส่วนใหญ่ควรใช้.แทนtext()ในนิพจน์ XPath


1
สิ่งที่ใช้ได้กับองค์ประกอบทุกประเภท? ฉันไม่สามารถรู้ได้ว่าข้อความนั้นอยู่ในปุ่ม ap, div, a span ฯลฯ หรือไม่
Andrea Bisello

6
@AndreaBisello คุณสามารถใช้//*[...]แทนได้
Thomas Dondorf

92

คุณสามารถใช้ตัวเลือก XPath กับเพจ $ x (นิพจน์) :

const linkHandlers = await page.$x("//a[contains(text(), 'Some text')]");

if (linkHandlers.length > 0) {
  await linkHandlers[0].click();
} else {
  throw new Error("Link not found");
}

ดูclickByTextในส่วนสำคัญนี้เพื่อดูตัวอย่างที่สมบูรณ์ ดูแลเรื่องการหลีกเลี่ยงเครื่องหมายคำพูดซึ่งค่อนข้างยุ่งยากกับนิพจน์ XPath


ยอดเยี่ยม - ฉันพยายามทำเพื่อแท็กอื่น แต่ไม่สามารถใช้งานได้ (li, h1, ... ) คุณจะทำอย่างไร?
Rune Jeppesen

3
@RuneJeppesen แทนที่//a[containsด้วย//*[containsเพื่อเลือกองค์ประกอบใด ๆ ไม่ใช่แค่องค์ประกอบ anchor ( a)
Unixmonkey

17

คุณยังสามารถใช้page.evaluate()เพื่อคลิกองค์ประกอบที่ได้รับจากdocument.querySelectorAll()ที่ถูกกรองโดยเนื้อหาข้อความ:

await page.evaluate(() => {
  [...document.querySelectorAll('.elements button')].find(element => element.textContent === 'Button text').click();
});

หรือคุณสามารถใช้page.evaluate()เพื่อคลิกองค์ประกอบตามเนื้อหาข้อความโดยใช้document.evaluate()และนิพจน์ XPath ที่เกี่ยวข้อง:

await page.evaluate(() => {
  const xpath = '//*[@class="elements"]//button[contains(text(), "Button text")]';
  const result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);

  result.iterateNext().click();
});

14

ได้ทำการแก้ไขอย่างรวดเร็วเพื่อให้สามารถใช้ตัวเลือก css ขั้นสูงเช่น ": contains (text)"

ดังนั้นการใช้ไลบรารีนี้คุณสามารถทำได้

const select = require ('puppeteer-select');

const element = await select(page).getElement('button:contains(Button text)');
await element.click()

5

นี่คือทางออกของฉัน:

let selector = 'a';
    await page.$$eval(selector, anchors => {
        anchors.map(anchor => {
            if(anchor.textContent == 'target text') {
                anchor.click();
                return
            }
        })
    });


1

ไม่มีไวยากรณ์ตัวเลือก css ที่รองรับสำหรับตัวเลือกข้อความหรือตัวเลือก combinator วิธีแก้ปัญหาของฉันสำหรับสิ่งนี้คือ:

await page.$$eval('selector', selectorMatched => {
    for(i in selectorMatched)
      if(selectorMatched[i].textContent === 'text string'){
          selectorMatched[i].click();
          break;//Remove this line (break statement) if you want to click on all matched elements otherwise the first element only is clicked  
        }
    });

1

มีคำตอบมากมายที่แนะนำcontainsแต่ฉันไม่เห็นแรงจูงใจใด ๆ สำหรับความไม่แม่นยำนี้เนื่องจากกรณีการใช้งานของ OP ซึ่งโดยลักษณะทั้งหมดเป็นการจับคู่แบบตรงทั้งหมดกับสตริงเป้าหมาย"Button text"และองค์ประกอบ<button>Button text</button>และองค์ประกอบ

ฉันต้องการใช้คำสั่งที่แม่นยำ[text()="Button text"]กว่าซึ่งจะหลีกเลี่ยงผลบวกปลอมเมื่อพูดว่า<button>Button text and more stuff</button>:

const [el] = await page.$x('//*[@class="elements"]//a[text()="Button text"]');
el && (await el.click());

สิ่งนี้คาดการณ์สถานการณ์ตรงกันข้ามของคำตอบนี้ซึ่งพยายามที่จะอนุญาตให้มากที่สุด

หลายคำตอบยังไม่ตรง.elementsตามข้อกำหนดของคลาสผู้ปกครอง


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