gpt4 book ai didi

node.js - 如何在 Node.js 中仅在所有网页抓取请求完成后才渲染页面?

转载 作者:太空宇宙 更新时间:2023-11-03 22:48:07 24 4
gpt4 key购买 nike

我正在使用 jsdom(Node.js 的网络抓取库)发出 1 到 10 个网络请求。事情是这样的:

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 是异步的,因此 res.render() 将在任何 jsdom 回调完成之前运行。

最佳答案

简单的解决方案

对于少量抓取可以采用的“天真的”解决方案是嵌套所有内容(在最后一次抓取的回调中启动每个抓取,最后一个回调包含渲染方法。)

scrape
cb: scrape
cb: scrape
cb: render all results

当然,这会变得乏味且难以辨认。 (一切都是串联运行,而不是并行运行,这不会很快。)

更好的解决方案

更好的解决方案是编写一个函数来计算返回结果的数量,并在所有结果返回时调用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);
}
);
});

最佳解决方案

您应该真正学习使用现有的并行/异步库,而不是编写自己的库,正如@almypal 在您的问题评论中所建议的那样。

使用async,您可以做一些更简洁的事情,如文档中所述:https://github.com/caolan/async#parallel

或者,如果您的所有抓取实际上在结果页面中查找相同的元素,您甚至可以对要抓取的 URL 数组进行并行映射: https://github.com/caolan/async#maparr-iterator-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 });
});
});

关于node.js - 如何在 Node.js 中仅在所有网页抓取请求完成后才渲染页面?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14643001/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com