【问题标题】:Recursive loop with promises crawler NodeJS带有 Promise 爬虫 NodeJS 的递归循环
【发布时间】:2019-04-28 16:20:06
【问题描述】:

我正在尝试使用递归循环和承诺来抓取网站。 但它失败了.. 它只对第一页发出请求,在第二页程序停止给我未处理的承诺拒绝警告

我有这三个 JS 文件:

  1. scrapeAll.js(是调用scrapePage.js的递归循环)
  2. scrapePage.js
  3. scrapeComponents.js

scrapeAll.js:

var indexPage = 0;

scrapePage(indexPage).then((json)=>{
    console.log(JSON.stringify(json, null, 4));
    if(indexPage === Number.MAX_SAFE_INTEGER){
        console.log("MAX SAFE INTEGER");
        return;
    }
    save(json);
    indexpage++;
    scrapePage(indexPage);
}).catch((data)=>{
    console.log(data);
    if(indexPage === Number.MAX_SAFE_INTEGER){
        console.log("MAX SAFE INTEGER");
        return;
    }
    indexPage++;
    scrapePage(indexPage);
});

ScrapePage.JS

let makeRequestCounter = 0;


function scrapePage(number) {
    return new Promise((resolve, reject) => {

        let url = URL + number;

        let options = {
            url: url,
            headers: {
                Host: SITE,
                Connection: "keep-alive",
                Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
                "Accept-Language": "it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7",
                "Cache-Control": "max-age=0",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36",
                "Cookie": restoreCookieToString()
            }
        };

        makeRequest(options).then((jsonData) => {
            resolve(jsonData);
        }).catch((error) => {
                //REQUEST_LIMIT_EXCEEDED
                if (error === CONSTANTS.REQUEST_LIMIT_EXCEEDED) {
                    reject(CONSTANTS.REQUEST_LIMIT_EXCEEDED);
                }

                //ALREADY_EXIST
                else if (error === CONSTANTS.ALREADY_EXIST) {
                    reject(CONSTANTS.ALREADY_EXIST);
                }

                else if (error === 404) {
                    reject("no data found at this page");
                }

                //error can beeconnrefused or econnreset
                else if (error.code !== undefined) {

                    //econnrefused
                    if (error.code === CONSTANTS.ECONNREFUSED) {
                        reject("WRONG_URL", url);
                    }

                    //econnreset
                    else if (error.code === CONSTANTS.ECONNRESET) {
                        console.log("\neconnreset error\n");
                        makeRequest(options);
                    }

                }
            }
        );
    });
}

function makeRequest(options) {
    return new Promise((resolve, reject) => {

        let json = {
            category: [],
            imgs: [],
            title: "",
            description: "",
            url: ""
        };


        if (makeRequestCounter === CONSTANTS.REQUEST_LIMIT) {
            reject(CONSTANTS.REQUEST_LIMIT_EXCEEDED);
        }

        makeRequestCounter++;

        console.log("request to: ", options.url);


        request(options, function (error, response, html) {
            if (error) {
                //error: possible econnreset econnrefused
                reject(error);

            } else {

                if (response.statusCode === 200) {
                    cookieSave(response.headers);


                    //---------- check if in db the url is already saved -------------//

                    check(response.request.uri.href, (err) => {
                        if (!err) {
                            reject(CONSTANTS.ALREADY_EXIST);
                        }
                    });

                    //----------finish checking, is new -------------------//


                    //GETTING TITLE

                    title(html, json_recipe).then((json) => {

                            //GETTING category

                            category(html, json).then((json) => {

                                    //GETTING images

                                    imgs(html, json).then((json) => {


                                        description(html, json).then((json) => {


                                                    json.url = response.request.uri.href;

                                                    resolve(json);

                                            //description error
                                        }).catch((error) => {
                                            console.log(error);
                                        });

                                        //images error
                                    }).catch((error) => {
                                        console.log(error);
                                    });

                                //category error
                            }).catch((error) => {
                                console.log(error);
                            });
                            //title error
                        }
                    ).catch((error) => {
                        console.log(error);
                    });
                }

                //no data in this page
                if (response.statusCode === 404) {
                    reject(response.statusCode);
                }
            }

        });
    });
}

scrapeComponents.js

...

function description(html, json) {
return new Promise((resolve, reject) => {

    const $ = cheerio.load(html);

    let description = $('.submitter__description').text().trim();

    json.description = JSON.parse(description);

    resolve(json);

});

}
...

错误:

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): no data found at this page

程序发出第一个请求并在 scrapeAll.js 正确返回 scrapePage(indexPage = 1)。 第二次我的程序与第一次完全相同,但是什么时候返回到 scrapeAll.js(reject("no data found at this page"); in ScrapePage.js)程序以错误结束。 两个页面都没有数据,但程序也失败了,好的页面只保存了第一个。 我认为我在承诺方面犯了一个大错误。 非常感谢你们。

【问题讨论】:

  • 我在这里看不到递归。 scrapePage().then(scrapePage).catch(scrapePage) 不是递归,makeRequest().then(...).catch(makeRequest) 也不是。
  • 代码将受益于大量的修复和改进。

标签: javascript node.js loops recursion promise


【解决方案1】:

问题是您在scrapeAll.js 中对scrapePage(indexPage) 的一次或多次调用失败。您不能像使用其他代码那样递归调用承诺,因此您还需要在附加调用中使用 .then.catch。在其他调用中添加.catch 将使您能够看到真正的失败源。

scrapePage(indexPage)
  .then((json)=>{
    console.log(JSON.stringify(json, null, 4));
    if(indexPage === Number.MAX_SAFE_INTEGER){
      console.log("MAX SAFE INTEGER");
      return;
    }
    save(json);
    indexpage++;
    scrapePage(indexPage).catch(e => console.log(e));
  })
  .catch((data)=>{
    console.log(data);
    if(indexPage === Number.MAX_SAFE_INTEGER){
      console.log("MAX SAFE INTEGER");
      return;
    }
    indexPage++;
    scrapePage(indexPage).catch(e => console.log(e));
});

【讨论】:

    【解决方案2】:

    您对 scrapPage 函数的调用仅运行一次,并且您没有反复调用它。您可能必须使用函数在迭代中调用它。更新你的 scrapeAll.js:

        function callScrapPage() {
        var indexPage = 0;
        while (indexPage < Number.MAX_SAFE_INTEGER) {
            scrapePage(indexPage).then((json) => {
                console.log(JSON.stringify(json, null, 4));
                save(json);
                indexpage++;
             }
           }
       }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-20
      • 2018-08-13
      • 1970-01-01
      • 1970-01-01
      • 2017-08-10
      • 1970-01-01
      • 1970-01-01
      • 2015-11-14
      相关资源
      最近更新 更多