我正在开发一款可以抓取网站并将数据公开为其他API的应用。我使用Node,Express,request,cheerio。我好像在数组中得到正确的值..但是在返回之前......数组仍然是空的(在请求函数范围之外)
我无法锻炼我所缺少的东西。你能看看我的代码并告诉我吗?
app.js中的我指定了路线
app.use('/timetable', timetable.timetable(url));
in timetable.js
var classes = require('../lib/classes');
var schedule = require('../lib/schedule');
exports.timetable = function(timeTableURL) {
return function(req, res) {
request( timeTableURL, function srapeWebsite(error, response, html ) {
var webHtml = '';
var moreInfo = [];
if( !error && response.statusCode === 200 ) {
webHtml = cheerio.load(html);
}
// schedule.getInfo returns an array of json objects where
// json = {
// day,
// number,
// url
// }
var info = schedule.getInfo(webHtml);
for (var index = 0; index < info.length; index++) {
var json = info[index];
classes.getMoreInfo(json, function (moreInfoJson) {
//console.log shows correct information here
moreInfo.push(moreInfoJson);
};
}
// however moreInfo is empty here..hence not getting anything
res.json(moreInfo);
} );
};
};
想法是,对于数组中的每个json对象,从指定的url获取更多信息。
所以最终的结果是,
finalJson = {
day : ''
json : []
}
在classes.js
中exports.getMoreInfo = function (info, callback) {
var infoDay = info.day;
var infoNumber = parseInt(info.number);
var moreInfoURL = info.moreInfoUrl;
var stuff = [];
var moreInfo = {};
moreInfo.day = infoDay;
moreInfo.json = [];
if (infoNumber > 0 ) {
request(moreInfoURL, function(error, response,html) {
var moreInfoHtml = '';
if( !error && response.statusCode === 200 ) {
moreInfoHtml = cheerio.load(html) ;
}
var $ = moreInfoHtml;
$('table tbody tr').each ( function getWhatisNeeded () {
var json = getJson ( $(this) );
stuff.push(json);
});
moreInfo.json = stuff;
return callback(moreInfo);
});
}
}
答案 0 :(得分:3)
您对classes.getMoreInfo的调用是异步的。
你不会等待调用返回,并使用空数组调用res.json。
编辑: 如何解决这个问题?一种方法是使用promisses(参见例如https://github.com/malko/D.js)。
getMoreInfo应该返回一个延迟对象:
exports.getMoreInfo = function (info) {
var deferred = D();
...
request(..., function(){
deferred.resolve(moreInfo);
});
...
return deferred.promise;
}
和timetable.js应该或多或少看起来像这样:
var promises = [];
for (var index = 0; index < info.length; index++) {
var json = info[index];
promises.push(classes.getMoreInfo(json));
}
D.all(promises).this(function(array_of_results){
res.json(array_of_results);
}
我没有测试代码,所以我可能在这里有一个错误,但这是如何解决node.js中的异步问题的一个很好的指导方针。 让自己熟悉承诺,解决这样的问题要容易得多。
答案 1 :(得分:0)
感谢提示'jonjon'..我使用Async.map来修复它(抱歉打算昨天发布它..但是你打败了它)..我也会尝试Promises ..我不妨学习这两种技术..
这就是我为解决它而做的事情。
在app.js
app.get('/timetable', timetable.timetable);
在timetable.js
var url = '...';
exports.timetable = function ( request, response ) {
//scrape html to get info
scrape.getHtml( url, function ( error, html ) {
// schedule.getInfo returns an array of json objects where
// json = {
// day,
// number,
// url
// }
var info = schedule.getInfo(cheerio.load(html));
//getmoreinfo
async.map(info, classes.getMoreInfo, function( error,moreInfo ) {
if(!error) {
response.json(moreInfo);
}else {
response.send("error encountered");
}
});
});
}
在classes.js
中 exports.getMoreInfo = function (info, callback) {
var infoDay = info.day;
var infoNumber = parseInt(info.number);
var moreInfoURL = info.moreInfoUrl;
var stuff = [];
var moreInfo = {};
moreInfo.day = infoDay;
moreInfo.json = [];
if (infoNumber > 0 ) {
request(moreInfoURL, function(error, response,html) {
var moreInfoHtml = '';
if( !error && response.statusCode === 200 ) {
moreInfoHtml = cheerio.load(html) ;
}
var $ = moreInfoHtml;
$('table tbody tr').each ( function getWhatisNeeded () {
var json = getJson ( $(this) );
stuff.push(json);
});
moreInfo.json = stuff;
return callback(null, moreInfo);
});
}
}
我正在接受我现在所追求的东西......无论如何它似乎需要5秒......无论如何都需要测试更多......