Puppeteer / Node.js只要有按钮就单击它-当它不再存在时,请开始操作

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)

AJC*_*C24 7

好的,这是我建议您为达到此目的而做的事情。我要忽略的是,您的数据总是有固定数量的行(也许将来会改变),而是通过持续单击来显示未知数量的数据行,从而为您设置“加载更多”按钮。

因此,您要做的第一件事就是设置一个方法,该方法确定是否在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由于元素不再存在,因此会触发请求中的异常。对我来说,解决此问题的最干净方法是catchclick指令上添加该空块,以便在发生这种情况时,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循环。

希望这可以帮助!