【问题标题】:Clicking the same button multiple times with puppeteer使用 puppeteer 多次单击同一个按钮
【发布时间】:2021-07-24 14:14:17
【问题描述】:

我试图在谷歌搜索食谱时点击“显示更多”按钮,多次使用 puppeteer。我让它在这样的 for 循环中工作,

for (let i = 0; i <= numberOfClicks; i++) {
    await page.click('div[aria-label="Show more"]')
    await page.waitForTimeout(800)
  }

但是,这会为每次点击增加 800 毫秒,我真的很想避免这种情况,以节省我正在拨打的电话的时间。

我尝试了各种不同的方法,并有类似的感觉,

for (let i = 0; i <= numberOfClicks; i++) {
    await page.waitForFunction(
      `document.querySelectorAll("g-inner-card a").length > ${i * 9}`
    )
    await page.click('div[aria-label="Show more"]')
  }

应该做我需要的,因为谷歌从 3 个食谱开始,每次点击“显示更多”时都会打开 9 个,但是,到目前为止,我只能让它点击一次,然后用这种方法挂起。

任何帮助将不胜感激,谢谢。

【问题讨论】:

  • 您能否分享相关的 URL,以便我自己运行和测试代码?不过,"g-inner-card a" 看起来像是一个糟糕的选择器。如果g-inner-card 是一个类,它应该是".g-inner-card a"。每当您在浏览器中运行代码时,最好在控制台中手动测试它或添加listeners,这样您就可以看到记录的错误。
  • 因此,作为一个例子,google.com/search?q=spaghetti+bolognese,您可以查看它们是否以某种方式具有g-inner-card 元素。而且,我在我的代码中的其他地方成功地使用了"g-inner-card a" 作为选择器,并且还在控制台中对其进行了检查。我认为单击“显示更多”时与移动按钮有关,并且需要放慢速度,因此它与waitForTimeout 一起使用。我只是不明白为什么waitForFunction 没有像我认为的那样工作
  • 感谢您的网址——是的,您是对的,这是一个合法的元素。你的代码对我有用。您最终想要获取哪些数据?我能想到的可能是您提出了太多请求并且它们限制了您,或者存在 A/B 情况或其他情况。
  • 我正在尝试获取食谱 URL 的数组。我有,但我真的不想使用waitForTimeout。您可以通过多少次点击来使用您的代码?对我来说,任何不止一次点击的东西都挂着。你能复制并粘贴你写的代码吗?也许我的代码中有一些早一点的东西导致它变慢或什么的。您能否再详细说明一下您所说的 A/B 情况是什么意思?
  • 我还有一个问题,当它多次点击时,有时它会出于某种原因点击其中一个实际的食谱。我正在努力调试,因为很难看到 puppeteer 在出错时实际做了什么

标签: javascript google-chrome web-scraping puppeteer


【解决方案1】:

Google 的网页往往很难被抓取。元素具有不可靠的自动生成的类和 ID,Google 似乎喜欢不必要的动画。

经过一番折腾,以下是最适合我的方法:

const puppeteer = require("puppeteer");

let browser;
(async () => {
  browser = await puppeteer.launch({headless: false});
  const [page] = await browser.pages();
  const url ="https://www.google.com/search?q=spaghetti+bolognese";
  await page.goto(url, {waitUntil: "networkidle0"});
  const showMoreSel = 'div[aria-label="Show more"]';

  while (await page.$eval(showMoreSel, el => el.offsetParent !== null)) {
    await page.evaluate(`document.querySelector('${showMoreSel}').click()`);
  }

  const urls = await page.$$eval("g-link a",
    els => els.map(e => e.getAttribute("href"))
  );
  console.log(urls);
})()
  .catch(err => console.error(err))
  .finally(async () => await browser.close())
;

这使用了两个技巧来绕过 Puppeteer 内置 DOM 操作抽象的缺点:

  • page.$('div[aria-label="Show more"]', {visible: true}) 似乎总是认为“显示更多”按钮是可见的,即使它不可见(表示卡片的结尾)。在eval 中使用the native approach 似乎是准确的。
  • page.click() 也失败了。此方法通过将鼠标悬停在选择器上,然后触发 mousedown 事件来工作,这可能会导致在尝试在紧密循环中使用动画移动物体时单击错误的物体。再次,evaluate 使用 a native browser click 进行救援。

假设 g-link a 与配方卡 1:1 映射可能很脆弱,因此您可能需要想出一种更可靠的方法来查找这些元素。

【讨论】:

  • 我需要做的就是将我的点击更改为本地浏览器点击,它现在可以工作了!我认为这与单击错误的位置有关,但后来我认为它使用了一个选择器,它是根据元素而不是鼠标移动来做的,真的很高兴知道。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2021-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-12
  • 1970-01-01
相关资源
最近更新 更多