【问题标题】:How to make multiple http requests from a Google Cloud Function (Cheerio, Node.js)如何从 Google Cloud Function(Cheerio,Node.js)发出多个 http 请求
【发布时间】:2019-01-16 02:40:41
【问题描述】:

我的问题:

我正在使用 Cheerio、Node.js 和 Google Cloud Functions 构建一个网络抓取工具。

问题是我需要发出多个请求,然后在调用 response.send() 之前将每个请求的数据写入 Firestore 数据库,从而终止函数。

我的代码需要两个循环:第一个循环是来自我的数据库的 url,每个循环都发出单独的请求。第二个循环是 Cheerio 使用 .each 从 DOM 中抓取多行表数据并为每一行单独写入。

我的尝试:

我已经尝试将每个请求推送到一组承诺,然后在调用 res.send() 之前等待所有承诺通过 promises.all() 解决,但我仍然对承诺有点动摇,而不是确定这是正确的方法。 (我已经让代码以这种方式适用于较小的数据集,但仍然不一致。)

我还尝试将每个请求创建为一个新的 Promise,并使用 async/await 等待来自 forEach 循环的每个函数调用,以便为每个请求留出时间并写入完全完成,以便之后我可以调用 res.send(),但是我发现 forEach 不支持 Async/await。

我尝试使用 p-iteration 模块解决这个问题,但因为它实际上不是 forEach 而是查询上的一个方法 (doc.forEach()),我认为它不会那样工作。

这是我的代码。

注意:

如前所述,这不是我尝试的所有内容(我删除了我的承诺尝试),但这应该表明我正在努力完成什么。

export const getCurrentLogs = functions.https.onRequest((req, response) => {


//First, I make a query from my db to get the urls 
// that I want the webscraper to loop through. 

const ref = scheduleRef.get()

.then((snapshot) => {

    snapshot.docs.forEach((doc) => {

        const scheduleGame = doc.data()
        const boxScoreUrl = scheduleGame.boxScoreURL

//Inside the forEach I call the request 
// as a function with the url passed in

        updatePlayerLogs("https://" + boxScoreUrl + "/");


    });

})

.catch(err => {
    console.log('Error getting schedule', err);
});


function updatePlayerLogs (url){

 
//Here I'm not sure on how to set these options 
// to make sure the request stays open but I have tried 
// lots of different things. 

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

   request(options)

        .then(($) => {


//Below I loop through some table data 
// on the dom with cheerio. Every loop 
// in here needs to be written to firebase individually. 


                $('.stats-rows').find('tbody').children('tr').each(function(i, element){


                    const playerPage = $(element).children('td').eq(0).find('a').attr('href');


                    const pts = replaceDash($(element).children('td').eq(1).text());
                    const reb =  replaceDash($(element).children('td').eq(2).text());
                    const ast =  replaceDash($(element).children('td').eq(3).text());
                    const fg =  replaceDash($(element).children('td').eq(4).text());
                    const _3pt =  replaceDash($(element).children('td').eq(5).text());
                    const stl =  replaceDash($(element).children('td').eq(9).text());
                    const blk =  replaceDash($(element).children('td').eq(10).text());
                    const to =  replaceDash($(element).children('td').eq(11).text());


                    const currentLog = {
                        'pts': + pts,
                        'reb': + reb,
                        'ast': + ast,
                        'fg':  fgPer,
                        '3pt': + _3ptMade,
                        'stl': + stl,
                        'blk':  + blk,
                        'to':  + to
                    }

                   //here is the write
                    playersRef.doc(playerPage).update({

                        'currentLog': currentLog

                    }) 
                    .catch(error => 
                        console.error("Error adding document: ", error + " : " + url)
                     );
                });

            })

        .catch((err) => {
            console.log(err); 
        });

    };

//Here I call response.send() to finish the function. 
// I have tried doing this lots of different ways but 
// whatever I try the response is being sent before all 
// docs are written.

   response.send("finished writing logs")

});

我尝试过的所有操作都会导致超出最后期限的错误(可能是因为我已经研究过配额限制,但我认为我不应该超过)或者代码未完成执行但显示的一些无法解释的错误我在日志中什么都没有。

请帮忙,有没有办法在我不理解的情况下使用 async/await?有没有办法使用 Promise 来让这变得优雅?

非常感谢,

【问题讨论】:

  • 只是一个反馈项——我认为如果你的代码经常被所有大写句子打断,它会更容易阅读。很难按原样遵循。但是可以肯定地告诉你,你根本没有正确处理承诺。在您发送最终响应之前,我没有看到任何代码行可以使您的代码中的每一个承诺都得到完全解决。
  • 抱歉,只是想通过添加大写来更加清晰,但我会对其进行编辑和调低。是的,我在这段代码中删除了我的承诺尝试,因为我认为没有它们可以更清楚地解释这个概念。
  • 问题是如何通过 promises 发出多个请求?
  • 是的,这样写比较好。

标签: node.js firebase web-scraping google-cloud-firestore google-cloud-functions


【解决方案1】:

也许看看这样的东西。它使用Bluebird promisesrequest-promise library

const Promise = require('bluebird');
var rp = require('request-promise');

const urlList = ['http://www.google.com', 'http://example.com']

async function getList() {
  await Promise.map(urlList, (url, index, length) => { 

    return rp(url)
      .then((response) => {

        console.log(`${'\n\n\n'}${url}:${'\n'}${response}`);
        return;
      }).catch(async (err) => {
        console.log(err);
        return;

      })


  }, {
    concurrency: 10
  }); //end Promise.map

}

getList();

【讨论】:

  • 哇,这看起来很有希望。我不知道你能做到这一点!
  • 你是否有理由包含 //eslint-disable-line,并评论 // return new Promise(resolve => {
  • 该评论来自我复制的其他代码,因此在这里没有意义。我现在将其删除以避免混淆。 @DarkhorseFantasySports
  • “return new promise”是否也应该被评论?
  • 不,错过了那个,这没有意义。我现在把它拿出来了。
猜你喜欢
  • 2019-01-27
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 2020-11-19
  • 2021-06-18
  • 1970-01-01
  • 2021-04-26
  • 1970-01-01
相关资源
最近更新 更多