【问题标题】:Requests for multiple pages with puppeteer使用 puppeteer 请求多个页面
【发布时间】:2020-04-24 10:35:15
【问题描述】:

我正在尝试从具有 puppeteer 的动态内容(电子邮件和公司名称)的许多站点(来自数组的链接)获取信息。我使用“for”循环来迭代带有链接的数组,对每个站点执行page.goto...,等到站点加载完毕,等待几秒钟以获取动态内容,然后开始执行请求。但我已经完成了第一个和最后一个请求(承诺解决)。其他承诺不会返回给我动态内容。我应该怎么做才能解决这个问题?谢谢

let puppeteer = require('puppeteer');

(async() => {
const browser = await puppeteer.launch();
let page = await browser.newPage();
const url = 'https://abcdsite.com/';
let arrayNames = ['first','second','third','abcd'];
for(let i=0;i<await arrayNames.length;){
    let nameUrl = await arrayNames[i];
    if (i<4){
      let temp1;
      console.log(`begin for ${nameUrl}`);
      await page.goto(`${url}${nameUrl}`, { waitUntil: 'load' })
          .then(()=>{
            return new Promise(res=>{
              //wait content dynamic load
              setTimeout(()=>{
                temp1 = page.evaluate(() => {
                  return new Promise(resolve => { // <-- return the data to node.js from browser
                    let name = document.querySelector('h1').innerHTML;
                    let email = document.getElementsByClassName('sidebar-views-contacts h-card vcard')[0]
                        .children[2].children[0].children[0].innerHTML;
                    resolve(email);
                  });
                });
                res(temp1);
              },7000);

            })
      })
          .then((res)=>{
            i++;
            console.log(`https://abcdsite.com/${nameUrl}`,temp1);
          });
    }
    else{
      break
    }
  }
})();

【问题讨论】:

  • 你想并行还是串行??
  • @Saeeed 首先我尝试了并行方式,但我得到了所有结果,例如pending-promises((

标签: node.js puppeteer


【解决方案1】:

我认为这对你有帮助。

1) 创建一个异步函数来请求和解析您的数据

2) 创建一个并行任务数组。

let puppeteer = require('puppeteer');

async function makeRequest(page, url, nameUrl) {
  await page.goto(`${url}${nameUrl}`, { waitUntil: 'load' });

  setTimeout(() => {
    const userEmail = await page.evaluate(() => {
      let name = document.querySelector('h1').innerHTML;
      let email = document.getElementsByClassName('sidebar-views-contacts h-card vcard')[0]
        .children[2].children[0].children[0].innerHTML;

      return email;
    });

    return Promise.resolve(userEmail);
  }, 7000);
}

(async () => {
  const browser = await puppeteer.launch();
  let page = await browser.newPage();
  const url = 'https://abcdsite.com/';
  let arrayNames = ['first', 'second', 'third', 'abcd'];

  let tasks = [];
  for (let i = 0; i < arrayNames.length; i++) {
    tasks.push(makeRequest(page, url, arrayNames[i]));
  }

  Promise.all(tasks)
    .then((res) => {
      for (let i = 0; i < arrayNames.length; i++) {
        console.log(`https://abcdsite.com/${arrayNames[i]}`, res[i]);
      }
    });

})();

系列解决方案

更多信息请阅读this

for (let i = 0; i < arrayNames.length; i++) {
  let temp = await makeRequest(page, url, arrayNames[i]);
  console.log(`https://abcdsite.com/${arrayNames[i]}`, temp);
}

【讨论】:

  • 它写信给我Possible EventEmitter memory leak detected. 11 Symbol(Events.FrameManager.FrameDetached) listeners added to [FrameManager]. Use emitter.setMaxListeners() to increase limit
  • 您要获取多少页?检查this 问题及其答案。 @diesel94
  • 我尝试加载 1600 页。在您的文章中,他们谈到了 slimerJS。我尝试添加require('events').EventEmitter.prototype._maxListeners = 1700;,但没有成功(
  • 1600 页!!好多啊。您应该使用另一种方法。尽快得到它们很重要吗?你可以做系列。或者您可以在每个任务中获取 N 个页面! @diesel94
  • 不,如果需要,我可以等一个小时)我意识到很难做到这一点,所以我想以任何方式做到这一点
【解决方案2】:

puppeteer 的page.goto 函数有多个参数可以用来确保页面完全加载。请参阅文档here。 此外,您可以使用page.waitFor 方法等待几秒钟。请参阅文档here

这里有一个我认为可能对你有用的简单示例:

const puppeteer = require('puppeteer')

const url = 'https://stackoverflow.com/'
const arrayNames = ['tags', 'users', 'jobs', 'questions'];

(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()

  const data = {}
  for (const nameUrl of arrayNames) {
    const fullUrl = `${url}${nameUrl}`
    console.log(`begin for ${fullUrl}`)
    await page.goto(fullUrl, { waitUntil: 'networkidle0' }) // check networkidle0 parameter and others here: https://pptr.dev/#?product=Puppeteer&version=v2.1.1&show=api-pagegotourl-options
    await page.waitFor(2000) // wait 2 seconds to allow a full login. Optional
    const pageData = await page.evaluate(() => {
      const name = document.querySelector('h1').innerText
      const pageTitle = document.querySelector('title').innerText
      // get whatever data you need to get from the page.
      return { name: name, title: pageTitle }
    })
    console.log('\t Data from page: ', pageData)
    data[fullUrl] = pageData
  }
  console.log(data)
})()

这不会并行运行所有站点,但您可以使用该示例进行操作。 而不是“等待”await page.evaluate 部分,您可以获取数组中的所有承诺,然后使用 await Promise.all([listOfPromises])

【讨论】:

  • 我已经尝试过这种方式,但它并没有为所有数据提供答案,仅针对来自站点的数据(及时到达),对于另一个站点,它不是及时到达的动态数据,我当数据尚未加载到站点时,仅获取存根“...”。我得到那样的东西abcdsite.com/spurit ⋯ abcdsite.com/senticode ⋯ abcdsite.com/instinctools ⋯ abcdsite.com/codemaster info@codemaster.by abcdsite.com/dewpoint ⋯ abcdsite.com/01d ⋯ abcdsite.com/12devs ⋯ abcdsite.com/onepoint ⋯
  • tempPage.waitFor(7000); 根本不适合我(
  • 我已经用 {headless: false} 进行了检查
  • 嗨@diesel94,我不确定我是否理解您的问题。也许您可以提供一个可以复制的 URL 示例。也许,您在 page.evaluate 函数中运行的 javascript 并不适用于所有页面。提供的示例应该适用于任何有效的页面。
  • 它适用于所有页面,但始终适用于不同的页面)somitimes 为 20%,sometomes 为 50、90%。示例:dev.by/onepoint; dev.by/spirit 等
猜你喜欢
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 2017-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多