rea*_*ble -1 javascript node.js puppeteer
有一个网页包含许多不断更新的数据行。
行的数目是固定的,因此旧的行会循环输出,并且不会存储在任何地方。
该页面由“加载更多”按钮分解,该按钮将显示直到所有存储的行都显示在页面上。
我需要在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);
}
Run Code Online (Sandbox Code Playgroud)
但我收到此错误:
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.
Run Code Online (Sandbox Code Playgroud)
好的,这是我建议您为达到此目的而做的事情。我要忽略的是,您的数据总是有固定数量的行(也许将来会改变),而是通过持续单击来显示未知数量的数据行,从而为您设置“加载更多”按钮。
因此,您要做的第一件事就是设置一个方法,该方法确定是否在UI中显示“加载更多”按钮。您想通过编写如下方法来做到这一点:
const isElementVisible = async (page, cssSelector) => {
let visible = true;
await page
.waitForSelector(cssSelector, { visible: true, timeout: 2000 })
.catch(() => {
visible = false;
});
return visible;
};
Run Code Online (Sandbox Code Playgroud)
传入所需的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);
}
Run Code Online (Sandbox Code Playgroud)
这将连续检查该按钮在您的UI中是否可见,如果显示该按钮,请单击该按钮,然后重复该过程,直到不再显示该按钮为止。这样可以确保在继续测试脚本的其余部分之前,所有数据行都将显示在UI中。
您还需要catch
对上述click
操作进行阻止。这样做的原因是headless
模式移动非常快。有时用户界面太快而无法跟上它的步伐。通常,在“显示更多”按钮的最后显示中,该isElementVisible
方法将在UI更新以消除该按钮的存在之前执行,因此true
实际上在现在不再显示选择器时返回该方法。然后,click
由于元素不再存在,因此会触发请求中的异常。对我来说,解决此问题的最干净方法是catch
在click
指令上添加该空块,以便在发生这种情况时,click
动作仍将干净地绕过而不会导致整个测试失败。
更新1:
您只是错误地使用了CSS选择器。您的选择器应为:
const cssSelector = '.u-field-button Button-button-18U-i'; // This is your CSS selector for the element
Run Code Online (Sandbox Code Playgroud)
您不需要为此使用evaluate
方法。
更新2:
好的,我已经添加了一些改进,我已经在几个不同的站点上对该代码进行了广泛的测试,发现我自己的逻辑并不适合单击所有按钮的“一刀切”方法,因此这是可能是为什么您要获得这些例外。我已经完成所有更改,更新了原始答案。
只是一个快速注:我已经更新了这两个isElementVisible
方法,并在while
循环。
希望这可以帮助!