如何点击元素与文字在木偶

是否有任何方法(没有在 API 中找到)或解决方案来点击元素与文本?

例如,我有 html:

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

我想点击文本包装的元素(点击内部的按钮) ,比如:

Page.click('Button text', '.elements')
172693 次浏览

您可以对 $x (表达式)使用 XPath 选择器:

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 表达式来说有点棘手。

我的解决办法是:

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

解决办法就是

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

您还可以使用 page.evaluate()单击从 document.querySelectorAll()获得的、经过文本内容过滤的元素:

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

或者,您可以使用 document.evaluate()和相应的 XPath 表达式,根据元素的文本内容,使用 page.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();
});

提出了快速解决方案,能够使用高级 CSS 选择器,如“ : 包含(文本)”

所以使用这个 图书馆你可以

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


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

长话短说

这个 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()不包括子节点的文本

下面是 contains(., 'Text')的 XPath 表达式,它对元素本身(包括它的子节点)起作用:

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

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

没有支持 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
}
});

我不得不:

await this.page.$eval(this.menuSelector, elem => elem.click());

由于 OP 的用例似乎与目标字符串 "Button text"<button>Button text</button>text()完全匹配,因此它似乎是正确的方法,而不是不那么精确的 contains()

虽然 托马斯的论点很有道理contains时,有子元素,避免假阴性,使用 text()避免一个假阳性时,按钮是,说,<button>Button text and more stuff</button>,这似乎只是一个可能的情况。拥有这两种工具非常有用,因此您可以根据具体情况选择更合适的工具。

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

请注意,许多其他的答案错过了 .elements父类的要求。

另一个 XPath 函数是 [normalize-space()="Button text"],它“从字符串中去除前导空格和尾随空格,用一个空格替换空格字符序列”,在某些情况下可能很有用。

另外,使用 waitForXPath通常也很方便,它会等待并返回与 XPath 匹配的元素,如果在指定的超时时间内没有找到该元素,则会抛出:

const xp = '//*[@class="elements"]//button[text()="Button text"]';
const el = await page.waitForXPath(xp);
await el.click();

使用木偶师12.0.1,我可以做到以下几点:

await page.click("input[value='Opt1']"); //where value is an attribute of the element input
await page.waitForTimeout(1000);


await page.click("li[value='Nested choice 1']"); //where value is an attribute of the element li after clicking the previous option
await page.waitForTimeout(5000);

您可以只使用查询选择器。

await page.evaluate(() => {
document.querySelector('input[type=button]').click();
});

编辑

你可以给你的按钮一个 className,然后用它来选择按钮元素,因为你知道你要点击的是什么:

await page.evaluate(() => {
document.querySelector('.button]').click();
});