首页 > 解决方案 > 如何在 Web 抓取时使用 Promise 和 async/await 发出多个嵌套的 http 请求

问题描述

下面我有一个 Node.js 函数,它向不同的 url 发出一系列请求,然后对于每个 url,我使用 Cheerio 网络抓取库循环遍历 dom 上的元素并创建一个子数组。在每个请求结束时(在子数组已满之后),我想将该数组的内容推送到一个更大的数组中,该数组超出了请求范围。

我正在尝试的方法似乎不起作用。看起来我无法从 .then 块内访问“allPlayers”。

function readPlayers(teamUrls){

    const allPlayers = [];

    teamUrls.forEach((teamUrl, i) => { 

        const options = {
            gzip: true,
            uri: teamUrl,
            Connection: 'keep-alive',
            transform: function (body) {
                return cheerio.load(body);
            }
        };

        request(options)
        .then(($) => {

            const team = [];

                $('tbody').children('tr').each(function(j, element){            

                     const playerName = $(element).children('td').eq(1).children('span').eq(1).find('a').text().trim();

                     const player = { 'playerName': playerName };

                     team.push(player);

                 });

            allPlayers.push(team);

        }).catch(err => console.log("error: " + err)) );

    });

}

所以我想知道重写此代码以使请求工作并用结果填充外部数组(allPlayers)的最佳方法。

我已经考虑尝试将整个请求直接推送到外部数组中,但无济于事。

在这个例子中,我使用 request-promise 来发出请求。

我已经研究过使用 Promise.map,我认为它适合这种情况。然后我会返回整个请求(我认为),但我不完全理解在这种情况下我在做什么......或者它是否会起作用。

任何人都可以解释这种情况下的范围,为什么我不能像我正在尝试的那样做。

非常感谢

标签: javascriptnode.jshttppromise

解决方案


您必须记住,当您使用异步函数时,您无法返回到同步代码执行。

这是您可以做到的方法之一。它将并行获取所有玩家:

async function readPlayers(teamUrls) {
   const playerPromises = teamUrls.map((teamUrl, i) => {
    const options = {
      gzip: true,
      uri: teamUrl,
      Connection: 'keep-alive',
      transform: function(body) {
        return cheerio.load(body);
      }
    };
    return request(options)
  });

  const players = await Promise.all(playerPromises);
  return players.reduce((allPlayers, $) =>{
    const team = [];
    $('tbody').children('tr').each(function(j, element) {
      const playerName = $(element).children('td').eq(1).children('span').eq(1).find('a').text().trim();
      const player = { playerName: playerName };
      team.push(player);
    });
    allPlayers.push(team);
    return allPlayers;
  },[])
}

你可以使用它await readPlayers(array)readPlayers(array).then(allteamplayers=>{...})

注意:在当前代码中,它将是一个二维数组,[[{p1:p1}..], [{p2:p2}..]] 等


推荐阅读