使用 waitForSelector 方法在 Puppteer JS 中更改类名时获取元素

问题描述 投票:0回答:1

我需要等待一个元素改变类。

问题是当我使用

waitForSelector
函数时,它不起作用,因为没有新元素添加到 DOM 中。然而,
<div>
元素改变了它的类名。

等待元素直到其类名更改,或等到某个类名出现的正确方法是什么?

我当前的代码:

import type { NextApiRequest, NextApiResponse } from "next";
const puppeteer = require("puppeteer");
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const browser = await puppeteer.launch({
    executablePath:
      "../../../../../../Program Files (x86)/Google/Chrome/Application/chrome.exe",
    headless: false,
  });
  const page = await browser.newPage();

  await page.goto("https://www.craiyon.com/", {
    timeout: 0,
    waitUntil: "domcontentloaded",
  });
  await page.waitForTimeout(1000);
  await page.type(".svelte-1g6bo9g", "sausage");
  await page.click("#generateButton");
  const test = await page.waitForSelector(
    ".h-full.w-full.cursor-pointer.rounded-lg.border.border-medium-blue.object-cover.object-center.transition-all.duration-200.hover:scale-[0.97].hover:border-2.hover:border-grey",
    {
      timeout: 0,
    }
  );

  await browser.close();
  console.log(test);
  res.status(200).json({ test: "test" });
}

这是稍后更改的类名:

.h-full.w-full.cursor-pointer.rounded-lg.border.border-medium-blue.object-cover.object-center.transition-all.duration-200.hover:scale-[0.97].hover:border-2.hover:border-grey

最后,这是我想要获取的类名:

.grid.grid-cols-3.gap-1.sm:gap-2

javascript node.js web-scraping next.js puppeteer
1个回答
2
投票

我相信你误解了

waitForSelector
。它不关心元素是新创建的还是已经存在并且有新的类修改。两者都是 DOM 突变,并且将注册为匹配项。

您可以等待您想要存在的选择器,而不是使用等待消失的旧选择器。一旦选择器准备好,

waitForSelector
就会立即解析,无论它如何进入 DOM 或位于哪个元素上。

如果你想等待某些东西消失或改变,你可以使用

waitForFunction
,这是
waitForSelector
的更通用版本。

此外,

:
表示伪选择器——它在技术上是有效的,但与
.sm:gap-2
不匹配。您可以将该类排除在外,或者使用建议的属性样式选择器在此评论中,但需要注意的是,这些选择器可能过于挑剔——如果顺序发生变化,它将失败。

暂时忽略这一部分似乎没问题,我们可以从响应中获取 URL,我猜这是我们最关心的:

const puppeteer = require("puppeteer"); // ^19.6.3

const url = "<Your URL>";

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  await page.goto(url, {waitUntil: "domcontentloaded"});
  await page.type("#prompt", "sausage");
  const imgUrls = new Set();
  const responsesArrived = Promise.all(
    [...Array(9)].map(() =>
      page.waitForResponse(
        res => {
          if (
            res.request().resourceType() === "image" &&
            res.url().startsWith("https://img.craiyon.com") &&
            res.url().endsWith(".webp") &&
            !imgUrls.has(res.url())
          ) {
            imgUrls.add(res.url());
            return true;
          }
        },
        {timeout: 120_000}
      )
    )
  );
  await page.click("#generateButton");
  const responses = await responsesArrived;
  console.log([...imgUrls]);
  const grid = await page.waitForSelector(
    ".grid.grid-cols-3.gap-1"
  );
  await grid.screenshot({path: "test.png"});
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

建议:

  • 尽量避免
    waitForTimeout
    。它已被弃用并导致竞争条件,要么减慢脚本速度,要么使其随机失败。 Puppeteer 的文档建议不要使用它。
  • 切勿使用
    timeout: 0
    ,尤其是在调试脚本时。没有理由永远阻止。如果选择器失败,并且您的脚本从未报告失败发生的位置,而是挂起,那么您会错过重要的诊断信息。如果您确实必须等待某些事情,否则您的计算机会爆炸,请将等待时间设置为 10 分钟、一天或一周(如果您确实希望某件事需要那么长时间),但不要无限期。如果是关键任务,您可以接住投掷并重试该操作。
  • 避免长选择器。它们通常很脆弱,因为它们对页面上的结构或类假设太多。这个页面有点敌意,提供了一些高质量的元素挂钩,但它仍然值得记住。通常认为最好通过用户可见的属性(例如角色和文本)进行选择。
  • 该网站加载了大量垃圾资源,因此您可以通过阻止您不需要的所有内容来加快速度并节省资源。您可以使用 page.on("request", req => console.log(req.url())
     查看所有 URL,然后系统地阻止与获取结果无关的 URL。

披露:我是链接博客文章的作者。

© www.soinside.com 2019 - 2024. All rights reserved.