提问人:Woden 提问时间:11/15/2023 最后编辑:Woden 更新时间:11/15/2023 访问量:22
Puppeteer:page.click 和 page.evaluate click 回退
Puppeteer: page.click and page.evaluate click fallbacks
问:
如标题所述,大多数情况下 page.click() 有效。但是,有时它会单击并且页面不会对操作做出反应。有两个详细的方案:
- page.click() 不起作用并引发错误。在这种情况下,我可以回退以正确评估点击。
- page.click() 确实有效,并且不会抛出错误。页面对点击没有反应。在这种情况下,我不知道如何回退到另一种点击方法。
评估点击也有可能不起作用。因此,我认为最好的解决方案是在无法点击时回退到对方。这是我的代码:
async clickElement(
page: Page,
projectName: string,
title: string,
selector: string,
timeout = 1000,
preventNavigation = false
): Promise<boolean> {
Logger.log(selector, 'CSSClickStrategy.clickElement');
// Ensure the selector is present before proceeding
// it could be a navigation before awaiting the selector
await Promise.race([
page.waitForSelector(selector, { timeout, visible: true }),
page.waitForNavigation({ timeout }),
]);
//TODO: how to determine to switch between the two click methods
// Add navigation prevention if required
if (preventNavigation) {
this.preventNavigationOnElement(page, selector);
return this.evaluateClick(page, projectName, title, selector);
} else {
// const result = await this.normalClick(page, projectName, title, selector);
const result = await this.evaluateClick(
page,
projectName,
title,
selector
);
if (!result) {
// return await this.evaluateClick(page, projectName, title, selector);
return await this.normalClick(page, projectName, title, selector);
} else {
return result;
}
}
}
private async preventNavigationOnElement(page: Page, selector: string) {
await page.evaluate((sel) => {
const element = document.querySelector(sel);
if (element) {
element.addEventListener('click', (e) => e.preventDefault());
}
}, selector);
}
private async evaluateClick(
page: Page,
projectName: string,
title: string,
selector: string,
timeout = 10000
): Promise<boolean> {
try {
await Promise.race([
page.evaluate((sel) => {
const element = document.querySelector(sel) as HTMLElement;
element?.click();
}, selector),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout exceeded')), timeout)
),
]);
Logger.log(
`Clicked using page.evaluate for selector: ${selector}`,
'CSSClickStrategy.clickElement'
);
return true;
} catch (error) {
Logger.error(error.message, 'CSSClickStrategy.evaluateClick');
return false;
}
}
private async normalClick(
page: Page,
projectName: string,
title: string,
selector: string,
timeout = 10000
): Promise<boolean> {
try {
await Promise.race([
page.click(selector, { delay: 100 }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout exceeded')), timeout)
),
]);
Logger.log(
`Clicked using page.click for selector: ${selector}`,
'CSSClickStrategy.clickElement'
);
return true;
} catch (error) {
Logger.error(error.message, 'CSSClickStrategy.clickElement');
return false;
}
}
非常感谢任何帮助。
答:
0赞
Woden
11/15/2023
#1
我已经想出了一种根据某些条件相互回退的方法。例如,我发现大多数时候评估点击都可以工作,但在第三方电子商务支付网关上不起作用。因此,我在支付页面上使用 page.click()。否则,默认情况下使用评估点击。另一个发现是,在隐身上下文下有一个选项卡,评估点击无法正常工作。这是我的解决方案代码,基于条件和遇到错误时相互回退:
async clickElement(
page: Page,
projectName: string,
title: string,
selector: string,
timeout = 1000,
preventNavigation = false
): Promise<boolean> {
Logger.log(selector, 'CSSClickStrategy.clickElement');
const domain = new URL(
this.sharedService.getProjectDomain(projectName, {
absolutePath: undefined,
name: title,
})
).hostname;
// Ensure the selector is present and visible before proceeding
await Promise.race([
page.waitForSelector(selector, { timeout, visible: true }),
page.waitForNavigation({ timeout }),
]);
// Determine the click method based on conditions
// only one page means checking datalayer; two pages mean checking with gtm preview mode
// if the current page is not the same as the domain, then it's a third-party gateway
const useNormalClick =
(await page.browserContext().pages()).length === 1 ||
!page.url().includes(domain);
if (preventNavigation) {
this.preventNavigationOnElement(page, selector);
}
if (useNormalClick) {
return await this.attemptClick(
page,
projectName,
title,
selector,
this.normalClick
);
} else {
return await this.attemptClick(
page,
projectName,
title,
selector,
this.evaluateClick
);
}
}
async attemptClick(
page: Page,
projectName: string,
title: string,
selector: string,
clickMethod: (
page: Page,
projectName: string,
title: string,
selector: string
) => Promise<boolean>
) {
const result = await clickMethod.call(
this,
page,
projectName,
title,
selector
);
if (!result) {
// Fallback to the other click method
return await (clickMethod === this.normalClick
? this.evaluateClick
: this.normalClick
).call(this, page, projectName, title, selector);
}
return result;
}
// other private methods are in the question content
以下是我的启动设置以供参考:
const browser = await puppeteer.launch({
headless: headless === 'new' ? 'new' : false,
defaultViewport: null,
ignoreHTTPSErrors: true,
args: [
'--window-size=1440,900',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--disable-gpu',
],
});
评论
.click()