问题描述
我正在构建一个使用node,request和cheerio的网络抓取器。
我编写的函数将一个数字作为参数,在for循环中用作索引,然后循环中的每个数字对应于包含博客文章索引的网站上的url。从那里,该函数返回每个URL上每个博客文章的标题,每个文章标题中包含的href,然后运行另一个请求,返回每个帖子页面中包含的每个href,使用相应文章标题的href作为输入值。因此,我在终端中的输出应采用以下格式:
Title: Some Blog Post Title 1
Link: Some Blog Post Link 1
Blog Post Links: List of Links on Blogs Page 1
Title: Some Blog Post Title 2
Link: Some Blog Post Link 2
Blog Post Links: List of Links on Blogs Page 2
但是相反,它像这样出来:
Title: Some Blog Post Title 1
Link: Some Blog Post Link 1
Title: Some Blog Post Title 2
Link: Some Blog Post Link 2
Giant list of blog post links
因此,我的代码具有功能性,因为它可以为我检索所有正确的信息,但格式不正确。当前的输出对我没有帮助,因为我需要能够分辨出每个页面对应的链接,而不是庞大的链接列表。 我已经研究了我的问题,并且我很确定这是由于代码的异步性质而发生的。我的功能与here提出的问题非常相似,但是我的不同之处在于,除了循环之外,还有另一个请求正在运行,第二个请求使用第一个请求的输出作为输入。 所以我的问题是如何重新格式化我的代码,以使我的输出按期望的顺序返回?
function scrapeUrls(num) {
for (var i = 1; i <= num ; i++) {
request(`https://www.website.com/blog?page=${i}`,(error,response,html) => {
if(!error && response.statusCode == 200) {
const $ = cheerio.load(html);
$('.group-right').each((i,el) => {
const articleTitle = $(el)
.find('h2')
.text();
const articleLink = $(el)
.find('a')
.attr('href');
console.log(`Title: ${articleTitle}\nLink: ${articleLink}\nBlog Post Links:`)
request(`https://www.website.com/${articleLink}`,html) => {
if(!error && response.statusCode == 200) {
const $ = cheerio.load(html);
$('.main-container').each((i) => {
links = $('a');
$(links).each(function(i,link) {
console.log($(link).text() + ':\n ' + $(link).attr('href'));
})
})
}
})
});
}
})
}
}
解决方法
正如@Dshiz指出的那样,如果您想保留订单,则需要await
承诺。我建议您使用node-fetch
而不是request
来返回实际承诺为awaited
:
let cheerio = require('cheerio');
let fetch = require('node-fetch');
function getArticlesLinks(html) {
const $ = cheerio.load(html);
let articles = [];
$(".group-right").each((i,el) => {
const articleTitle = $(el).find("h2").text();
const articleLink = $(el).find("a").attr("href");
console.log(`Title: ${articleTitle}\nLink: ${articleLink}\nBlog Post Links:`);
articles.push(articleLink);
});
return articles;
}
function getLinks(html) {
const $ = cheerio.load(html);
$(".main-container").each((i) => {
links = $("a");
$(links).each(function (i,link) {
console.log(
$(link).text() + ":\n " + $(link).attr("href")
);
});
});
}
async function scrapeUrls(num) {
for (var i = 1; i <= num; i++) {
// Fetch the page
let pageResponse = await fetch(`https://www.website.com/blog?page=${i}`);
if (pageResponse.ok) {
let pageHtml = await pageResponse.text();
// ^ HERE
// Extract articles' links
let articles = getArticlesLinks(pageHtml);
// For each article fetch and extract links
for (let a of articles) {
let articleResponse = await fetch(`https://www.website.com/${a}`);
if (articleResponse.ok) {
let articleHtml = await articleResponse.text();
// ^ HERE
getLinks(articleHtml);
}
}
}
}
}
scrapeUrls(4)
.then(() => console.log('done'))
.catch(console.error)
在这里,我将scrapeUrls
函数转换为async
,因此可以在每个await
循环中使用for of
。