有一个网页,其中包含许多不断更新的数据行。
行数是固定的,因此旧行被循环出并且不会存储在任何地方。
该页面由一个“加载更多”按钮分解,该按钮将显示直到所有存储的行都显示在页面上。
我需要在Puppeteer / Node.js中编写一个脚本,单击该按钮,直到该按钮不再存在于页面上为止。
之后
...阅读页面上的所有文本。 (我已经完成了脚本的这一部分。)
我是Puppeteer的新手,不确定如何设置。任何帮助将不胜感激。
编辑:
我添加了此块:
const cssSelector = await page.evaluate(() => document.cssSelector('.u-field-button Button-button-18U-i'));
// Click the "load more" button repeatedly until it no longer appears
const isElementVisible = async (page, cssSelector) => {
await page.waitForSelector(cssSelector, { visible: true, timeout: 2000 })
.catch(() => {
return false;
});
return true;
};
let loadMoreVisible = await isElementVisible(page, cssSelector);
while (loadMoreVisible) {
await page.click(cssSelector);
loadMoreVisible = await isElementVisible(page, cssSelector);
}
但是我收到此错误:
Error: Evaluation failed: TypeError: document.cssSelector is not a function
at __puppeteer_evaluation_script__:1:17
at ExecutionContext.evaluateHandle (/Users/reallymemorable/node_modules/puppeteer/lib/ExecutionContext.js:124:13)
at process.internalTickCallback (internal/process/next_tick.js:77:7)
-- ASYNC --
at ExecutionContext.<anonymous> (/Users/reallymemorable/node_modules/puppeteer/lib/helper.js:144:27)
at ExecutionContext.evaluate (/Users/reallymemorable/node_modules/puppeteer/lib/ExecutionContext.js:58:31)
at ExecutionContext.<anonymous> (/Users/reallymemorable/node_modules/puppeteer/lib/helper.js:145:23)
at Frame.evaluate (/Users/reallymemorable/node_modules/puppeteer/lib/FrameManager.js:439:20)
at process.internalTickCallback (internal/process/next_tick.js:77:7)
-- ASYNC --
at Frame.<anonymous> (/Users/reallymemorable/node_modules/puppeteer/lib/helper.js:144:27)
at Page.evaluate (/Users/reallymemorable/node_modules/puppeteer/lib/Page.js:736:43)
at Page.<anonymous> (/Users/reallymemorable/node_modules/puppeteer/lib/helper.js:145:23)
at /Users/reallymemorable/Documents/scripts.scrapers/squarespace.ip.scraper/squarespace5.js:32:34
at process.internalTickCallback (internal/process/next_tick.js:77:7)
(node:8009) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:8009) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
答案 0 :(得分:4)
好吧,这是我建议您实现的目的。我要忽略的是,您的数据总是有固定的行数(也许将来会改变),而是通过连续单击显示未知的数据行数来设置您的数据“加载更多”按钮。
因此,您要做的第一件事就是设置一个方法,该方法确定是否在UI中显示“加载更多”按钮。您想通过编写如下方法来做到这一点:
const isElementVisible = async (page, cssSelector) => {
let visible = true;
await page
.waitForSelector(cssSelector, { visible: true, timeout: 2000 })
.catch(() => {
visible = false;
});
return visible;
};
一旦传入所需的CSS选择器(在本例中为“加载更多”按钮的选择器),则此方法将在显示按钮时返回true
,如果没有显示则返回false
。
您希望超时为2000
,因为您要不断检查是否显示了此按钮。如果未显示,则超时将默认为30000
,而且时间太长,无法让您的代码挂起等待。因此,我发现2000
是一个不错的折衷方案。 catch
块的目的是捕获不再显示该元素时将引发的错误-由于您试图到达按钮的位置,因此您要忽略引发错误的事实不再显示。您知道点击X次后将不会显示它。没关系。因此,您需要catch
错误才能完全绕开该错误。
然后,下一步是执行类似的操作,以使您的代码继续单击“加载更多”按钮,直到不再单击(即显示)为止:
let loadMoreVisible = await isElementVisible(page, selectorForLoadMoreButton);
while (loadMoreVisible) {
await page
.click(selectorForLoadMoreButton)
.catch(() => {});
loadMoreVisible = await isElementVisible(page, selectorForLoadMoreButton);
}
这将连续检查按钮在您的UI中是否可见,如果显示,请单击它,然后重复该过程直到不再显示该按钮。这样可以确保在继续测试脚本的其余部分之前,所有数据行都将显示在UI中。
您还需要在catch
操作上使用click
块,如上所示。其原因是headless
模式移动非常快。有时用户界面太快而无法跟上它的步伐。通常,在“显示更多”按钮的最后一个显示中,isElementVisible
方法将在UI更新以消除该按钮的存在之前执行,因此实际上,当返回选择器现在不再显示。然后,由于元素不再存在,因此触发了true
请求的异常。对我来说,解决此问题的最干净方法是在click
指令上添加空的catch
块,这样,如果发生这种情况,click
操作仍将干净地绕过而不会失败整个测试。
更新1:
您只是错误地使用了CSS选择器。您的选择器应为:
click
您无需为此使用const cssSelector = '.u-field-button Button-button-18U-i'; // This is your CSS selector for the element
方法。
更新2:
好的,我已经添加了一些改进,我已经在几个不同的站点上对该代码进行了广泛的测试,发现我自己的逻辑并不适合单击“一刀切”的方法所以这可能就是为什么您遇到这些异常的原因。我已经完成所有更改,更新了原始答案。
快速说明:我已经更新了evaluate
方法和 isElementVisible
循环。
希望这会有所帮助!