【问题标题】:Using Request.js and Cheerio.js in Node/Express return empty array在 Node/Express 中使用 Request.js 和 Cheerio.js 返回空数组
【发布时间】:2017-05-05 03:44:30
【问题描述】:

我正在 Express 中使用 Request.js 和 Cheerio.js 构建一个简单的抓取工具。现在我只是在寻找网站的标题。我将列表放在一个数组中,而不是一个一个地抓取一个网站。我解析它们,然后使用 Cheerio.js 找到网站的标题。当我控制台记录标题时,它们会很好,但我想最终在 html 页面上显示它们。请注意,我对编程很陌生,所以如果您能提供详细的反馈,那将非常有帮助(以下是我一直在处理的代码)。提前致谢!

function parseSites(urls) {
var parsedSites = [];
urls.forEach(function(site) {
        request(site, function(err, res, body) {
            if(err) {
                console.log(err);
            } else {
                var $ = cheerio.load(body);
                parsedSites.push($('title').text());
                }
            }    
        });             
    });
    return parsedSites;
}

【问题讨论】:

    标签: javascript node.js express asynchronous cheerio


    【解决方案1】:

    首先您需要了解异步代码和同步代码之间的区别。让我们看一个例子:

    function testFor() {
        for(let i=0;i<5;++i){
            console.log(i);
        }
    }
    

    -

    console.log('start:');
    testFor();
    console.log('end:');
    
    // Here you get the expected output because this code is synchronous.
    //output:
        start:
        0
        1
        2
        3
        4
        end:
    

    -

    console.log('start:');
    setTimeout(testFor,1000);
    console.log('end:');
    
    // Here you don't get your expected output because setTimeout is asynchronous .
    //output:
        start:
        end:
        0
        1
        2
        3
        4
    
    1. 首先是console.log('start:');叫做。
    2. 然后设置超时(testFor,1000); (但它是异步的并且调用 将在 1 秒后执行)。
    3. console.log('end:'); 被调用。
    4. 最后 1 秒后,testFor() 被执行,它 打印 0 1 2 3 4

    下一点就是你的代码有错误!

    function parseSites(urls) {
        var parsedSites = [];
        urls.forEach(function(site) {
            request(site, function(err, res, body) {
                if(err) {
                    console.log(err);
                } else {
                    var $ = cheerio.load(body);
                    parsedSites.push($('title').text());
                }
            //} ! THIS bracket should be removed
            });
        });
        return parsedSites;
    }
    

    所以你的问题是,forEach 循环中的“请求”是一个异步函数,一旦有来自网页的响应,它将调用回调函数“function(err, res, body)”。

    我的解决方案:

    'use strict'
    
    const cheerio = require('cheerio');
    const request = require('request');
    const async = require('async');
    
    const urls = ['http://stackoverflow.com/','http://hackaday.com/','https://www.raspberrypi.org/','https://cheerio.js.org/'];
    
    //SOLUTION 1: do what you need to do when all calls are done using recursion
    let i=0;
    let parsedSites = [];
    parseSites(urls[i],parsedSites);
    function finalCall(sites) {
        console.log(sites);
    }
    function parseSites(site,parsedSites) {
        ++i;
        request(site, function(err, res, body) {
            if(err) {
                console.log(err);
            } else {
                let $ = cheerio.load(body);
                let title = $('title').text();
                console.log(title);
                parsedSites.push(title);
            }
            if(i<urls.length){
                parseSites(urls[i],parsedSites);// recursive call;
            }
            else{
                finalCall(parsedSites);// when all sites are done.
            }
        });
        //return parsedSites;// cant return! we are in async calls!
    }
    
    
    //SOLUTION 2: do what you need to do when all calls are done using 'async'
    parseSites(urls);
    function finalCall(sites) {
        console.log(sites);
    }
    function parseSites(urls) {
        let parsedSites = [];
        async.each(urls,function parseSite(site, callback) {
            request(site, function (err, res, body) {
                if (err) {
                    callback(err);
                } else {
                    let $ = cheerio.load(body);
                    parsedSites.push($('title').text());
                    callback();
                }
            })
        },function (err) {
            if(err) console.log(err);
            else finalCall(parsedSites);
        });
    }
    

    Async github page

    Async example

    【讨论】:

    • 非常感谢您的详细反馈。一直在玩 async.js,但仍在尝试掌握异步代码。这是超级超级有用!
    【解决方案2】:

    请参考下面的代码以获得有效的实现

    var request = require('request-promise')
        var cheerio = require("cheerio")
    
        function parseSites(urls, callback) {
            var parsedSites = [];
            var promiseList = urls.map(getPage)
    
            Promise.all(promiseList).then(function (data) {
                callback(data.map(parse))
            })
    
            return parsedSites;
        }
    
        function getPage(url) {
    
            return request.get(url)
        }
    
        function parse(body) {
            console.log("parsing body")
            var $ = cheerio.load(body);
            return $('title').text()
        }
    
        parseSites(['https://www.google.com','https://www.facebook.com'],function(data) {
            console.log(data)
        })
    

    【讨论】:

    • 非常感谢您的反馈。目前正在学习有关帮助处理异步代码的承诺!
    猜你喜欢
    • 2015-05-02
    • 2021-10-22
    • 2019-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-25
    • 2022-12-09
    • 2020-11-22
    相关资源
    最近更新 更多