如何触发点击事件的链接上的选项卡列表与木偶的页面



我一直在寻找解决这个问题的方法,并在这里找到了一些专注于单击元素的方法,但没有一个允许基于链接单击元素。

使用puppeteer,我将循环遍历一组制表符

<div role="tablist">
<div><a href="#one" tabindex="-1" role="tab" aria-selected="false" class="">One</a></div>
<div><a href="#two" tabindex="-1" role="tab" aria-selected="false" class="">Two</a></div>
<div><a href="#three" tabindex="0" role="tab" aria-selected="true" class="icn-cv-down">three</a></div>
</div>

,能够抓取url或哈希,但得到错误link.click() is not a function。我相信这是由于Puppeteer不能像JS那样触发点击,但不确定前进的方向:

let tabs = await page.evaluate(() => {
var tab = [...document.querySelectorAll('[role="tablist"] a')].map(
(el) => el.hash
);
return tab;
});
let components = [];
if (tabs) {
tabs.forEach((link, index) => {
setTimeout(() => {
link.click();
components.push(
[...document.querySelectorAll(".ws-compid")]
.map((component) => component.innerText)
.filter((el) => el !== "")
);
}, 200 * index);
});
}
console.log(components);

我相信我需要一个异步函数能够触发点击事件,但不确定。这应该能够单击每个选项卡的href值,然后将值从页面推送到组件数组中。

我无法运行您的页面来查看实际的行为是什么,但是基于所提供的有限信息,以下是我拼凑一个可以适应您的用例的工作示例的最佳尝试:

const puppeteer = require("puppeteer"); // ^19.1.0
const html = `
<div role="tablist">
<div><a href="#one" tabindex="-1" role="tab" aria-selected="false" class="">One</a></div>
<div><a href="#two" tabindex="-1" role="tab" aria-selected="false" class="">Two</a></div>
<div><a href="#three" tabindex="0" role="tab" aria-selected="true" class="icn-cv-down">three</a></div>
<div class="ws-compid"></div>
</div>
<script>
document.querySelectorAll('[role="tablist"] a').forEach(e => 
e.addEventListener("click", () => {
document.querySelector(".ws-compid").textContent = e.textContent;
})
);
</script>`;
let browser;
(async () => {
browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.setContent(html);
const components = await page.evaluate(() =>
Promise.all(
[...document.querySelectorAll('[role="tablist"] a')].map(
(e, i) =>
new Promise(resolve =>
setTimeout(() => {
e.click();
resolve(
[...document.querySelectorAll(".ws-compid")]
.map(component => component.innerText)
.filter(e => e)
);
}, 200 * i)
)
)
)
);
console.log(components); // => [ [ 'One' ], [ 'Two' ], [ 'three' ] ]
})()
.catch(err => console.error(err))
.finally(() => browser?.close());

将浏览器代码翻译成Puppeteer可能会出错:异步加载,bot检测,iframes, shadow DOM,仅举几个障碍,所以如果这不起作用,我需要一个可复制的示例。

虽然你声称你的原始代码可以工作,但我不认为这是可能的。该模式可以归结为:

const tabs = [..."ABCDEF"];
let components = [];
tabs.forEach((link, index) => {
setTimeout(() => {
components.push(link);
}, 200 * index);
});
console.log(components); // guaranteed to be empty
// added code
setTimeout(() => {
console.log(components.join("")); // "ABCDEF"
}, 2000);

您可以看到console.log(components)setTimeouts完成之前运行。只有在添加了人工延迟之后,我们才看到components像预期的那样填充。请参阅规范线程如何从异步调用返回响应?一个解决方案是像我上面所做的那样承诺回调。

还请注意,睡眠200毫秒并不理想。你当然可以用waitForFunction来加快速度。


在评论中,你分享了一个有类似标签的网站,但是你不需要点击任何东西来访问每次点击后显示的文本:

const puppeteer = require("puppeteer");
let browser;
(async () => {
browser = await puppeteer.launch();
const [page] = await browser.pages();
const url = "https://www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-manual.html";
await page.goto(url, {waitUntil: "domcontentloaded"});
const text = await page.$$eval(
'#ex1 [role="tabpanel"]',
els => els.map(e => e.textContent.trim())
);
console.log(text);
})()
.catch(err => console.error(err))
.finally(() => browser?.close());

所以很有可能这是一个xy问题

最新更新