首页 > 解决方案 > 自定义函数未定义 Puppeteer

问题描述

我制作了这个自定义函数并将其放在全局之外,这通常会起作用。我还尝试将它移动到主要的异步 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() 然后你 goto(url)。然后我添加了这个;

等待 page.exposeFunction("grabDomConvertNodlistToArray", grabDomConvertNodlistToArray);

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


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

解决方案 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;
  });

标签: javascriptweb-scrapingpuppeteer

解决方案


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();

推荐阅读