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

时间:2018-11-08 02:48:45

标签: 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);
  }

但是我收到此错误:

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.

1 个答案:

答案 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循环。

希望这会有所帮助!