gpt4 book ai didi

javascript - 在 NodeJS 中使用嵌套回调时遇到问题

转载 作者:行者123 更新时间:2023-12-03 08:28:56 25 4
gpt4 key购买 nike

我正在编写一个程序,该程序可以抓取网站中的链接,然后抓取这些链接以获取信息。为了抓取网站,必须先登录。因此顺序是:登录 -> 抓取链接索引 -> 抓取信息链接

登录函数的回调打印一个空数组{ results: [], hasMore: true },所以我的代码有问题(抓取部分有效):

var request = require('request');
var request = request.defaults({jar: true}); // necessary for persistent login
var cheerio = require('cheerio');

var url1 = "https://example.org/torrents/browse/index/";
var loginUrl = "https://example.org/user/account/login/";

var credentials = {
username: 'user1',
password: 'passpass'
};

login(function (result) {
console.log(result);
});

function login(callback) {
request.post({
uri: loginUrl,
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: require('querystring').stringify(credentials)
}, function(err, res, body){
if(err) {
console.log("Login error");
return;
}
scrapeTorrents(url1, function (result) {
callback(result);
});
});
}

function scrapeTorrents(url, callback) {
request(url, function(err, res, body) {
if(err) {
console.log("Main scrape error");
return;
}
var links = []
var $ = cheerio.load(body);
$('span.title').each(function(i, element){
var title = $(this);
var a = $(this).children().eq(0);
var detailsUrl = a.attr('href');
//console.log(detailsUrl);
links.push(detailsUrl);
});
scrapeTorrentDetails(links, function (result) {
callback(result);
});
});
}

function scrapeTorrentDetails(links, callback) {
var results = [];

function getDetails(url) {
request(url, function(err, res, body) {
if(err) {
console.log("Detail scrape error");
return;
}
console.log("Scraping: " + url);
var $ = cheerio.load(body);
var tds = $('td');
var title = $(tds).get(1).firstChild.data;
var hash = $(tds).get(3).firstChild.data.trim();
var size = $(tds).get(9).firstChild.data;
// console.log(tds.length);
if (tds.length > 23) {
var rlsDate = $(tds).get(23).firstChild.data || '';;
var genres = $(tds).get(27).firstChild.data || '';;
var runtime = $(tds).get(31).firstChild.data || '';;
if ( $(tds).get(33).firstChild != null) {
var plot = $(tds).get(33).firstChild.data || '';;
}
var rating = $('#imdb_rating').parent().next().text() || '';; // of 10
var imdb_id = $('[name=imdbID]').get(0).attribs.value || '';;
var cover = $('#cover').children().eq(0).get(0).attribs.href || '';;
var thumb = $('[alt=Cover]').get(0).attribs.src || '';;
if (typeof cover == 'undefined') {
cover = thumb;
}
} else {
var rlsDate = "notfound";
var genres = "notfound";
var runtime = "notfound";
var plot = "notfound";
var rating = "notfound"; // of 10
var imdb_id = "notfound";
var cover = "notfound";
var thumb = "notfound";
}

var movie = {
type: 'movie',
imdb_id: imdb_id,
title: title,
year: rlsDate,
genre: genres,
rating: rating,
runtime: runtime,
image: thumb,
cover: cover,
synopsis: plot,
torrents: {
magnet: 'magnet:?xt=urn:btih:' + hash + '&tr=http://tracker.example.org:2710/a/announce',
filesize: size
}
};

results.push(movie);
});
}

for (var i=0; i<links.length; i++){
getDetails("https://example.org" + links[i]);
}

callback( {
results: results,
hasMore: true
});
}

也许 Q promise 会更好。我如何在上面的代码中实现它?

如果您想知道代码的用途,我计划修改 Popcorn-time 以使用另一个 torrent 跟踪器(没有 API)。

谢谢

最佳答案

这段代码的主要问题是:

for (var i=0; i<links.length; i++){
getDetails("https://example.org" + links[i]);
}

callback( {
results: results,
hasMore: true
});

getDetails() 是异步的,但您只需调用它 links.length 次并继续 - 就像它们都已完成一样。因此,在调用回调并尝试传递结果之前,getDetails() 中的任何请求都不会完成。但是,尚未填写任何结果,因此它们将为空。

您的代码中到处都有所有这些其他嵌套回调(根据需要),但您却在这个地方失败了。在使用结果调用最终回调之前,您需要知道所有 getDetails() 调用何时完成。

此外,您还必须决定是否可以并行调用所有 getDetails() 调用(所有调用同时进行),或者您真正想要做的是调用一个,等待它完成,然后调用下一个,依此类推...现在您将它们全部放在一起进行中,如果目标服务器不立即拒绝那么多请求,那么这可以工作。

<小时/>

有几种潜在的策略可以解决这个问题。

  1. getDetails() 添加回调,然后记录从 getDetails() 获得 links.length 回调的时间 并且仅当整个计数完成后才调用最终回调。

  2. 更改 getDetails() 以返回 promise 。然后,您可以使用诸如 links.map(getDetails) 之类的东西来创建一个 Promise 数组,然后您可以使用 Promise.all() 来了解它们何时全部完成.

就我个人而言,我会更改您的所有代码以使用 Promise,并且我会使用 Bluebird Promise 库,因为它的额外功能(例如 Promise.map())使这一切变得更加简单。

这里有一个修复程序,它向 getDetails() 添加回调,然后计算已完成的数量:

function scrapeTorrentDetails(links, callback) {
var results = [];

function getDetails(url, done) {
request(url, function(err, res, body) {
if(err) {
console.log("Detail scrape error");
done(err);
return;
}
console.log("Scraping: " + url);
var $ = cheerio.load(body);
var tds = $('td');
var title = $(tds).get(1).firstChild.data;
var hash = $(tds).get(3).firstChild.data.trim();
var size = $(tds).get(9).firstChild.data;
// console.log(tds.length);
if (tds.length > 23) {
var rlsDate = $(tds).get(23).firstChild.data || '';;
var genres = $(tds).get(27).firstChild.data || '';;
var runtime = $(tds).get(31).firstChild.data || '';;
if ( $(tds).get(33).firstChild != null) {
var plot = $(tds).get(33).firstChild.data || '';;
}
var rating = $('#imdb_rating').parent().next().text() || '';; // of 10
var imdb_id = $('[name=imdbID]').get(0).attribs.value || '';;
var cover = $('#cover').children().eq(0).get(0).attribs.href || '';;
var thumb = $('[alt=Cover]').get(0).attribs.src || '';;
if (typeof cover == 'undefined') {
cover = thumb;
}
} else {
var rlsDate = "notfound";
var genres = "notfound";
var runtime = "notfound";
var plot = "notfound";
var rating = "notfound"; // of 10
var imdb_id = "notfound";
var cover = "notfound";
var thumb = "notfound";
}

var movie = {
type: 'movie',
imdb_id: imdb_id,
title: title,
year: rlsDate,
genre: genres,
rating: rating,
runtime: runtime,
image: thumb,
cover: cover,
synopsis: plot,
torrents: {
magnet: 'magnet:?xt=urn:btih:' + hash + '&tr=http://tracker.example.org:2710/a/announce',
filesize: size
}
};

results.push(movie);
done();
});
}

var doneCnt = 0;
for (var i=0; i<links.length; i++){
getDetails("https://example.org" + links[i], function() {
++doneCnt;
if (doneCnt === links.length) {
callback( {
results: results,
hasMore: true
});
}
});
}

}

关于javascript - 在 NodeJS 中使用嵌套回调时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33425739/

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