如何在Puppeteer中单击带有文本的元素


86

是否有任何方法(在API中找不到)或解决方案来单击带有文本的元素?

例如,我有html:

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

我想单击包裹文本的元素(单击.elements中的按钮),例如:

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


找到答案了吗?
Shubham Batra

如果有帮助,我要执行单击的页面已加载jQuery,因此我能够&使用validate方法执行jQuery代码。
布兰迪托

Answers:


77

目前的顶级答案由tokland仅适用于文本节点,而不是与其他元素的内部节点。

简短答案

此XPath表达式将查询包含文本“按钮文本”的按钮:

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()返回包含两个文本(StartEnd)的列表,但contains仅检查第一个
  • //button[contains(text(), 'Middle')] 将不返回任何结果,因为text()不包括子节点的文本

这是XPath表达式contains(., 'Text'),适用于元素本身(包括其子节点):

  • //button[contains(., 'Start')]将同时返回两个按钮
  • //button[contains(., 'End')]将再次返回两个按钮
  • //button[contains(., 'Middle')] 将返回一个(最后一个按钮)

因此,在大多数情况下,在XPath表达式中使用.代替更有意义text()


为什么这不是最佳答案?很好的解释,非常感谢!
Gianlucca

1
适用于每种类型元素的东西?我不知道,如果文本是一个按钮,AP,一个div,跨度等内
安德烈Bisello

5
@AndreaBisello您可以//*[...]改用。
Thomas Dondorf '19

90

您可以将XPath选择器与page。$ x(expression)结合使用

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,...)您将如何做?
符文耶普森

3
@RuneJeppesen替换//a[contains//*[contains以选择任何元素,而不仅仅是锚(a)元素。
Unixmonkey

14

您还page.evaluate()可以点击从document.querySelectorAll()文本内容中过滤出来的元素:

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

或者,您可以使用和相应的XPath表达式page.evaluate()来基于元素的文本内容单击元素document.evaluate()

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

做出了快速的解决方案,以能够使用高级的CSS选择器,例如“:contains(text)”

所以使用这个库,你可以

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

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

4

这是我的解决方案:

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

4

解决方案是

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

0

没有针对文本选择器或combinator选项的受支持的CSS选择器语法,我的解决方法是:

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

有很多答案提示,contains但是鉴于OP的用例(从所有方面来看,它都是与目标字符串"Button text"和element的精确匹配),我认为这种不精确性没有任何动机<button>Button text</button>

我更喜欢使用更精确的[text()="Button text"],这样可以避免在按钮为false时出现误报<button>Button text and more stuff</button>

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

这预料到了该答案的相反情形,情形试图尽可能地宽松。

许多答案也错过了.elements家长课堂的要求。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.