簡體   English   中英

如何在 web 抓取時使用 promises 和 async/await 發出多個嵌套的 http 請求

[英]How to make multiple nested http requests with promises and async/await while web scraping

下面我有一個 Node.js function 向不同的 url 發出一系列請求,然后對於每個 url 我使用 Cheerio web 抓取庫循環遍歷 dom 上的元素並創建一個子數組。 在每個請求結束時(子數組已滿后),我想將該數組的內容推送到更大的數組,該數組在請求 scope 之外。

我正在嘗試的方法似乎沒有用。 看起來我無法從 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) 的最佳方法。

我研究過嘗試將整個請求直接推入外部數組,但無濟於事。

在此示例中,我使用請求承諾來發出請求。

我研究過使用 Promise.map,我認為它適合這種情況。 然后我會返回整個請求(我認為),但我不完全理解在那種情況下我在做什么......或者它是否有效。

任何人都可以解釋這種情況下的范圍界定,為什么我不能像我正在嘗試的那樣做。

非常感謝

您必須記住,當您使用異步 function 時,您不能 go 返回到同步代碼執行。

這是您可以做到的方法之一。 它將並行獲取所有玩家:

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}..]] 等

如果您使用 forEach,每個回調都將異步運行,您將無法等待它們。 您可以將其交換為 for 循環,將您的承諾收集到一個數組中,然后等待所有承諾的完成:

async function readPlayers(teamUrls) {
  const allPlayers = [];
  const allPromises = [];

  for (var i = 0; i < teamUrls.length; i++) {
    var teamUrl = teamUrls[i];

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

    allPromises.push(
      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))
    );

    // wait untill all the promises resolve
    await Promise.all(allPromises);

    console.log(allPlayers);

    return allPlayers;
  }
}

然后你可以通過等待你的 function 來獲得所有玩家:

var allPlayers = await readPlayers(teamUrls);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM