在 Node/Express 中使用 Request.js 和 Cheerio.js 返回空数组

Posted

技术标签:

【中文标题】在 Node/Express 中使用 Request.js 和 Cheerio.js 返回空数组【英文标题】:Using Request.js and Cheerio.js in Node/Express return empty array 【发布时间】: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;

【问题讨论】:

【参考方案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
    首先是console.log('start:');叫做。 然后设置超时(testFor,1000); (但它是异步的并且调用 将在 1 秒后执行)。 console.log('end:'); 被调用。 最后 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://***.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)
    )

【讨论】:

非常感谢您的反馈。目前正在学习有关帮助处理异步代码的承诺!

以上是关于在 Node/Express 中使用 Request.js 和 Cheerio.js 返回空数组的主要内容,如果未能解决你的问题,请参考以下文章

AWS Cognito 可以在 EB 上的 Node/Express/React 应用程序中使用吗?

如何在 Node/Express 中使用 CAC 对用户进行身份验证

Reactjs Stripe 支付不适用于 Node/Express

在 Node/Express 中使用 Request.js 和 Cheerio.js 返回空数组

在 Node/Express 中续集 - “没有这样的表:main.User”错误

Node.js - Express.js JWT 总是在浏览器响应中返回一个无效的令牌错误