Cách nhấp vào phần tử có văn bản trong Puppeteer


86

Có phương pháp nào (không tìm thấy trong API) hoặc giải pháp để nhấp vào phần tử có văn bản không?

Ví dụ tôi có html:

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

Và tôi muốn nhấp vào phần tử mà văn bản được bao bọc (nhấp vào nút bên trong .elements), như:

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

2
Đã chia sẻ câu trả lời tại đây: stackoverflow.com/a/47829000/6161265
Md. Abu Taher

Bạn đã tìm thấy câu trả lời?
Shubham Batra

Nếu nó hữu ích, trang mà tôi muốn thực hiện một lần nhấp vào đã tải jQuery nên tôi có thể & sử dụng phương thức đánh giá để thực thi mã jQuery.
Brandito

Câu trả lời:


78

Các đầu câu trả lời hiện bởi tokland chỉ hoạt động trên các nút văn bản và không trên các nút với các yếu tố khác bên trong.

Câu trả lời ngắn

Biểu thức XPath này sẽ truy vấn một nút có chứa văn bản "Văn bản nút":

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

Để tôn trọng <div class="elements">xung quanh các nút, hãy sử dụng mã sau:

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

Giải trình

Để giải thích tại sao việc sử dụng nút văn bản ( text()) là sai trong một số trường hợp, hãy xem một ví dụ:

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

Đầu tiên, hãy kiểm tra kết quả khi sử dụng contains(text(), 'Text'):

  • //button[contains(text(), 'Start')]sẽ trả về cả hai nút (như mong đợi)
  • //button[contains(text(), 'End')]sẽ chỉ trả về một nút (nút đầu tiên) như text()trả về danh sách có hai văn bản ( StartEnd), nhưng containssẽ chỉ kiểm tra nút đầu tiên
  • //button[contains(text(), 'Middle')] sẽ không trả về kết quả vì text()không bao gồm văn bản của các nút con

Dưới đây là các biểu thức XPath cho contains(., 'Text'), hoạt động trên chính phần tử bao gồm các nút con của nó:

  • //button[contains(., 'Start')]sẽ trả về cả hai nút
  • //button[contains(., 'End')]sẽ trả lại cả hai nút
  • //button[contains(., 'Middle')] sẽ trả lại một (nút cuối cùng)

Vì vậy, trong hầu hết các trường hợp, sẽ hợp lý hơn nếu sử dụng .thay vì text()trong biểu thức XPath.


tại sao đây không phải là câu trả lời hàng đầu? lời giải thích tốt đẹp cảm ơn rất nhiều!
Gianlucca

1
cái gì đó hoạt động với mọi loại phần tử? tôi không thể biết nếu văn bản là bên trong một nút, ap, một div, một khoảng, vv
Andrea Bisello

5
@AndreaBisello Bạn có thể sử dụng //*[...]thay thế.
Thomas Dondorf

90

Bạn có thể sử dụng bộ chọn XPath với trang. $ X (biểu thức) :

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");
}

Kiểm tra clickByTexttrong ý chính này để biết một ví dụ hoàn chỉnh. Nó xử lý các dấu ngoặc kép thoát, điều này hơi phức tạp với các biểu thức XPath.


Tuyệt vời - Tôi đã cố gắng làm điều đó cho các thẻ khác, nhưng không thể làm cho nó hoạt động. (li, h1, ...) Bạn sẽ làm điều đó như thế nào?
Rune Jeppesen,

3
@RuneJeppesen Thay thế //a[containsbằng //*[containsđể chọn bất kỳ phần tử nào, không chỉ một aphần tử anchor ( ).
Unixmonkey

14

Bạn cũng có thể sử dụng page.evaluate()để nhấp vào các phần tử thu được từ document.querySelectorAll()đó đã được lọc theo nội dung văn bản:

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

Ngoài ra, bạn có thể sử dụng page.evaluate()để nhấp vào một phần tử dựa trên nội dung văn bản của nó bằng cách sử dụng document.evaluate()và biểu thức XPath tương ứng:

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();
});

13

đã thực hiện giải pháp nhanh chóng để có thể sử dụng các bộ chọn css nâng cao như ": chứa (văn bản)"

vì vậy sử dụng thư viện này bạn có thể

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

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

4

Đây là giải pháp của tôi:

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

4

Giải pháp là

(await page.$$eval(selector, a => a
            .filter(a => a.textContent === 'target text')
))[0].click()

0

Không có cú pháp bộ chọn css được hỗ trợ cho bộ chọn văn bản hoặc một tùy chọn tổ hợp, công việc của tôi cho điều này sẽ là:

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  
        }
    });

0

Có rất nhiều câu trả lời gợi ý containsnhưng tôi không thấy bất kỳ động lực nào cho sự thiếu chính xác này vì trường hợp sử dụng của OP, theo tất cả các lần xuất hiện, là một kết hợp chính xác với một chuỗi mục tiêu "Button text"và một phần tử <button>Button text</button>.

Tôi muốn sử dụng chính xác hơn [text()="Button text"], tránh dương tính giả khi nút, chẳng hạn <button>Button text and more stuff</button>:

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

Điều này dự đoán kịch bản ngược lại của câu trả lời này cố gắng dễ dãi nhất có thể.

Nhiều câu trả lời cũng bỏ sót .elements yêu cầu của lớp phụ huynh.


-1

Tôi phải:

await this.page.$eval(this.menuSelector, elem => elem.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.