如何在Node.js中完成所有Web抓取请求后才呈现页面?
我正在使用jsdom (Node.js的networking抓取库)在1到10个Web请求之间的任何地方。 它是这样的:
app.get('/results', function(req, res) { jsdom.env( "http://website1.com", ["http://code.jquery.com/jquery.js"], function (errors, window) { // scrape website #1 } ); jsdom.env( "http://website2.com", ["http://code.jquery.com/jquery.js"], function (errors, window) { // scrape website #2 } ); jsdom.env( "http://website3.com", ["http://code.jquery.com/jquery.js"], function (errors, window) { // scrape website #3 } ); } res.render('results', { items: items }); }
在所有jsdom请求完成之后,我收集了所有我需要的信息之后,如何才运行res.render() ? 在同步世界中,这显然不会是一个问题,但由于JavaScript是asynchronous的,res.render()将在任何jsdomcallback完成之前运行。
天真的解决scheme
你可以用于less量刮擦的“天真”的解决scheme是嵌套一切(开始每个刮在最后一个刮的callback,最后一个callback包含渲染方法)。
scrape cb: scrape cb: scrape cb: render all results
当然,这变得单调乏味,难以辨认。 (所有东西都是串联的,不是平行的,不会很快)。
解决scheme更好
更好的解决scheme是编写一个函数来计算返回结果的数量,并在全部返回时调用render
。 这是一个实现:
function parallel_cb(total, finalCallback) { var done = 0; var results = []; return function(result) { done += 1; results.push(result); if (total == done) finalCallback(results); } }
在你的例子中使用它:
app.get('/results', function(req, res) { var myCallback = parallel_cb( sitesToScrape.count, // or 3 in this case function(items) {res.render('results', { items: items })}); jsdom.env( "http://nodejs.org/dist/", ["http://code.jquery.com/jquery.js"], function (errors, window) { // do some scraping myCallback(result_from_scrape); } ); jsdom.env( "http://nodejs.org/dist/", ["http://code.jquery.com/jquery.js"], function (errors, window) { // more scraping myCallback(result_from_scrape); } ); jsdom.env( "http://nodejs.org/dist/", ["http://code.jquery.com/jquery.js"], function (errors, window) { // even more scraping myCallback(result_from_scrape); } ); });
最好的解决scheme
您应该真正学会使用现有的并行/asynchronous库,如@almypal在您的问题的评论中所build议的。
与async
你可以做一些更整洁的文档中描述的: https : //github.com/caolan/async#parallel
或者,如果所有的刮擦实际上在结果页面中查找相同的元素,甚至可以对URL数组进行平行映射以进行刮擦: https : //github.com/caolan/async#maparr-iterator-callback
每个scrape都可以使用async并行方法提供的callback函数来返回它的结果。 最后的[可选]callback将包含您的调用与所有项目render
。
编辑:你要求的例子
这是你的代码,直接翻译成async
库:
var async = require("async"); app.get('/results', function(req, res) { async.parallel( // the first argument is an array of functions [ // this cb (callback) is what you use to let the async // function know that you're done, and give it your result function (cb) { jsdom.env( "http://nodejs.org/dist/", ["http://code.jquery.com/jquery.js"], function (errors, window) { // do some scraping // async's callback expects an error for the first // param and the result as the second param cb(null, result_from_scrape); //No error } ); }, function (cb) { jsdom.env( "http://nodejs.org/dist/", ["http://code.jquery.com/jquery.js"], function (errors, window) { // more scraping cb(null, result_from_scrape); } ); }, function (cb) { jsdom.env( "http://nodejs.org/dist/", ["http://code.jquery.com/jquery.js"], function (errors, window) { // even more scraping cb(null, result_from_scrape); } ); } ], // This is the "optional callback". We need it to render. function (err, results) { // If any of the parallel calls returned an error instead // of null, it's now in the err variable. if (err) res.render('error_template', {error: err}); else res.render('results', { items: results }); }); });