如何在 Puppeteer 中单击带有文本的元素
How to click on element with text in Puppeteer
有没有什么方法(在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')
您可以将 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");
}
查看 gist 中的 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()
to click elements obtained from document.querySelectorAll()
已按文本内容过滤:
await page.evaluate(() => {
[...document.querySelectorAll('.elements button')].find(element => element.textContent === 'Button text').click();
});
或者,您可以使用 page.evaluate()
to click an element based on its text content using 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();
});
快速解决方案可以使用高级 css 选择器,例如“:contains(text)”
所以使用这个 library 你可以
const select = require ('puppeteer-select');
const element = await select(page).getElement('button:contains(Button text)');
await element.click()
简答
此 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')]
将 return 两个 两个 节点(如预期的那样)
//button[contains(text(), 'End')]
只会 return 一个 个节点(第一个)作为 text()
returns 一个包含两个文本的列表( Start
和 End
), 但 contains
只会检查第一个
//button[contains(text(), 'Middle')]
会return没有结果因为text()
不包含子节点的文本
这里是 contains(., 'Text')
的 XPath 表达式,它作用于元素本身,包括它的子节点:
//button[contains(., 'Start')]
将 return 两个 两个 按钮
//button[contains(., 'End')]
将再次 return 两个 两个 按钮
//button[contains(., 'Middle')]
将return一个(最后一个按钮)
因此在大多数情况下,在 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()
似乎是正确的方法而不是 less-precise contains()
.
虽然 对于 contains
当有 sub-elements 时,避免误报,使用 text()
避免误报,例如,当按钮是 <button>Button text and more stuff</button>
,这似乎很可能是一种情况。手边同时拥有这两种工具很有用,因此您可以在 case-by-case 的基础上选择更合适的工具。
const xp = '//*[@class="elements"]//button[text()="Button text"]';
const [el] = await page.$x(xp);
await el?.click();
请注意,许多其他答案未满足 .elements
父 class 要求。
此外,使用 waitForXPath
等待,然后 returns,匹配 XPath 的元素通常很方便,如果在指定的超时时间内没有找到则抛出:
const xp = '//*[@class="elements"]//button[text()="Button text"]';
const el = await page.waitForXPath(xp);
await el?.click();
使用 puppeteer 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);
有没有什么方法(在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')
您可以将 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");
}
查看 gist 中的 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()
to click elements obtained from document.querySelectorAll()
已按文本内容过滤:
await page.evaluate(() => {
[...document.querySelectorAll('.elements button')].find(element => element.textContent === 'Button text').click();
});
或者,您可以使用 page.evaluate()
to click an element based on its text content using 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();
});
快速解决方案可以使用高级 css 选择器,例如“:contains(text)”
所以使用这个 library 你可以
const select = require ('puppeteer-select');
const element = await select(page).getElement('button:contains(Button text)');
await element.click()
简答
此 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')]
将 return 两个 两个 节点(如预期的那样)//button[contains(text(), 'End')]
只会 return 一个 个节点(第一个)作为text()
returns 一个包含两个文本的列表(Start
和End
), 但contains
只会检查第一个//button[contains(text(), 'Middle')]
会return没有结果因为text()
不包含子节点的文本
这里是 contains(., 'Text')
的 XPath 表达式,它作用于元素本身,包括它的子节点:
//button[contains(., 'Start')]
将 return 两个 两个 按钮//button[contains(., 'End')]
将再次 return 两个 两个 按钮//button[contains(., 'Middle')]
将return一个(最后一个按钮)
因此在大多数情况下,在 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()
似乎是正确的方法而不是 less-precise contains()
.
虽然 contains
当有 sub-elements 时,避免误报,使用 text()
避免误报,例如,当按钮是 <button>Button text and more stuff</button>
,这似乎很可能是一种情况。手边同时拥有这两种工具很有用,因此您可以在 case-by-case 的基础上选择更合适的工具。
const xp = '//*[@class="elements"]//button[text()="Button text"]';
const [el] = await page.$x(xp);
await el?.click();
请注意,许多其他答案未满足 .elements
父 class 要求。
此外,使用 waitForXPath
等待,然后 returns,匹配 XPath 的元素通常很方便,如果在指定的超时时间内没有找到则抛出:
const xp = '//*[@class="elements"]//button[text()="Button text"]';
const el = await page.waitForXPath(xp);
await el?.click();
使用 puppeteer 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);