【问题标题】:Custom Function Not Defined Puppeteer自定义函数未定义 Puppeteer
【发布时间】:2021-11-06 13:56:17
【问题描述】:

我制作了这个自定义函数并将它放在全局之外,这通常会起作用。我还尝试将它移动到主要的异步 puppeteer 函数中,但也不起作用。它是一个简单的功能。在每个页面评估函数中,我调用它并传递选择器。但是,它的说法没有定义并且承诺拒绝这很奇怪,因为该功能不是承诺......请帮助

const grabDomConvertNodlistToArray = (grabDomHtmlPath) => {
  // grabbing node list from html selector all
  const nList = document.querySelectorAll(grabDomHtmlPath);
  // converting nodelist to array to be returned
  const array = Array.from(nList);
  return array;
};

我尝试将函数转换为添加新参数页面的异步函数。然后我将 async 添加到我的评估函数中,然后将 puppeteer 页面作为参数传递,但仍然错误且无法正常工作。

const grabDomConvertNodlistToArray = async (page, grabDomHtmlPath) => {
  try {
    // grabbing node list from html selector all
    const nList = await page.document.querySelectorAll(grabDomHtmlPath);
    // converting nodelist to array to be returned
    const array = Array.from(nList);
    return array;
  } catch (error) {
    console.log(error);
  }
};

所以我有典型的 puppeteer 设置,你等待 browser.newPage() 然后转到 (url)。然后我添加了这个;

await page.exposeFunction("grabDomConvertNodlistToArray", grabDomConvertNodlistToArray);

将异步添加到我的评估回调函数中,即 async() => {}。但是在上面的评估函数中调用我的自定义函数时,由于某种原因它仍然不起作用。


找到了解决方案,但它对我不起作用。我正在获取 array.forEach 不是一种方法,它向我表明在我的 grabDomConvertNodlistToArray 函数中它没有抓取 nodeList 或将其转换为数组。如果是这样,那么 forEach 将是一个函数。

Solution 3

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(someURL);       

var functionToInject = function(){
    return 1+1;
}

var otherFunctionToInject = function(input){
    return 6
}

await page.exposeFunction("functionToInject", functionToInject)
await page.exposeFunction("otherFunctionToInject", otherFunctionToInject)

var data = await page.evaluate(async function(){
    console.log('woo I run inside a browser')
    return await functionToInject() + await otherFunctionToInject();
});

    return data

所以抹掉上面的两个函数,转换成下面我的函数使用。

const grabDomConvertNodlistToArray = (grabDomHtmlPath) => {
    // grabbing node list from html selector all
    const nList = document.querySelectorAll(grabDomHtmlPath);
    // converting nodelist to array to be returned
    const array = Array.from(nList);
    return array;
  };

运行我的 js 文件会导致 array.forEach 的错误不是一个奇怪的函数,因为如果函数按预期工作,我的评估函数中的 const 数组将是一个数组,因为它的 = 对上述函数是返回一个数组。所以.....idk 发生了什么认为它与 document.querySelectorAll() 行有关。

const rlData = async () => {
  const browser = await puppeteer.launch(
    {
      headless: true,
    },
    {
      args: ["--flag-switches-begin", "--disable-features=OutOfBlinkCors", "--flag-switches-end"],
    }
  );

const pageBodies = await browser.newPage();
  await pageBodies.goto("https://test.com/bodies", {
    waitUntil: "load",
  });

  const grabDomConvertNodlistToArray = (grabDomHtmlPath) => {
    // grabbing node list from html selector all
    const nList = document.querySelectorAll(grabDomHtmlPath);
    // converting nodelist to array to be returned
    const array = Array.from(nList);
    return array;
  };

  await pageBodies.exposeFunction("grabDomConvertNodlistToArray", grabDomConvertNodlistToArray);

  const rlBodyNames = await pageBodies.evaluate(async () => {
    // grabs all elements in html to make nodelist & converts it to an array
    const array = grabDomConvertNodlistToArray(".testbodies > div > h1");
    // push the data collected from array into data array and returned
    const data = [];
    array.forEach((element) => {
      data.push(element.textContent);
    });
    return data;
  });
}
rlData();

我想我将不得不将 document.querySelectorAll 功能从自定义函数中移出并返回到评估中。但是,制作该自定义函数的全部原因是为了减少多次使用相同的代码,因为我的整个爬虫有 238 行长,并且有很多重复性。无法调用像我这样的自定义函数对于重构相同的代码执行来说是可怕的。

我放弃了尝试让它发挥作用,并决定这样做。是的,如果你有更多的页面要抓取,它会使你的代码重复,所以你将多次使用相同的代码,这是我试图避免的,但是,puppeteer 更糟糕的是重构你的代码,可能是所述包的开发人员将添加轻松使用自定义函数的能力,就像我尝试的方式一样。

const testNames = await pageBodies.evaluate(() => {
    const nodeList = document.querySelectorAll(".test > div h2");
    const array = Array.from(nodeList);
    const data = [];
    array.forEach((element) => {
      data.push(element.textContent);
    });
    return data;
  });

【问题讨论】:

    标签: javascript web-scraping puppeteer


    【解决方案1】:

    exposeFunction() 不适合您的情况:公开的函数旨在在浏览器和 Node.js 上下文之间传输数据,因此可以将其封装在代码中,该代码可序列化和反序列化参数以及返回的数据和一些不可序列化的数据数据(作为 DOM 元素)可能会丢失。试试这个:

    const rlData = async () => {
      const browser = await puppeteer.launch(
        {
          headless: true,
        },
        {
          args: ["--flag-switches-begin", "--disable-features=OutOfBlinkCors", "--flag-switches-end"],
        }
      );
    
      const pageBodies = await browser.newPage();
    
      await pageBodies.evaluateOnNewDocument(() => {
        window.grabDomConvertNodlistToArray = function grabDomConvertNodlistToArray(grabDomHtmlPath) {
          // grabbing node list from html selector all
          const nList = document.querySelectorAll(grabDomHtmlPath);
          // converting nodelist to array to be returned
          const array = Array.from(nList);
          return array;
        }
      });
    
      await pageBodies.goto("https://test.com/bodies", {
        waitUntil: "load",
      });
    
      const rlBodyNames = await pageBodies.evaluate(() => {
        // grabs all elements in html to make nodelist & converts it to an array
        const array = grabDomConvertNodlistToArray(".testbodies > div > h1");
        // push the data collected from array into data array and returned
        const data = [];
        array.forEach((element) => {
          data.push(element.textContent);
        });
        return data;
      });
    }
    rlData();
    

    【讨论】:

    • 好吧,但是我的刮板工作方式是在 page.Bodies 完成之后我打开一个新页面并转到一个新 url 并使用不同的 html 路径重复顶部。所以这个功能只能在 pageBodies 上工作,对吧?然后将不得不在另一个页面中再次重复整个函数,如果我必须再重复 5 次,这将破坏重构代码的目的。我希望能够在我正在使用的所有页面中调用该函数,并且只在代码中使用一次。
    • 这应该是一个简单的解决方法,尽管只需将我所有的评估重命名为主浏览器,并且在每个完成后不要打开一个新的。然后我将能够在我的所有评估函数中调用该函数,因为它在同一个浏览器中并且只有一个浏览器而不是 5 个新浏览器。
    • 所以添加了你的函数代码,当悬停在函数上时它给了我这个错误!image
    • 猜猜这个错误并不重要,它仍然在终端 umm werid 中运行而没有错误。非常感谢您的帮助,这真的让我发疯了。此外,该方法的 puppeteer 文档并没有真正解释您可以像这样使用它。
    猜你喜欢
    • 1970-01-01
    • 2017-09-08
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 2020-01-09
    • 2022-01-26
    • 1970-01-01
    相关资源
    最近更新 更多