blo*_*pit 5 javascript node.js web-scraping promise
这是我尝试重构我的代码以正确利用承诺。整个程序是一个基本的网络爬虫。
这样做的挑战是尝试确保 lastStep 可以访问每个页面的 HTML 和 URL,因此我尝试返回一个对象nextStep().
我正在控制台记录 html 并且它已正确返回,但由于某种原因,承诺的记录如下:Promise { <pending> }。为什么会发生这种情况以及如何解决它?
谢谢你!
//Modules being used:
var cheerio = require('cheerio');
var json2csv = require('json2csv');
var request = require('request');
var moment = require('moment');
var fs = require('fs');
//harcoded url
var url = 'http://shirts4mike.com/';
//url for tshirt pages
var urlSet = new Set();
var remainder;
var tshirtArray = [];
const requestPromise = function(url) {
return new Promise(function(resolve, reject) {
request(url, function(error, response, html) {
if(error) return reject(error);
if(!error && response.statusCode == 200){
return resolve(html);
}
});
});
}
function scrape (url) {
return requestPromise(url)
.then(function(html) {
var $ = cheerio.load(html);
var links = [];
//get all the links
$('a[href*=shirt]').each(function(){
var a = $(this).attr('href');
//add into link array
links.push(url + a);
});
// return array of links
return links;
});
}
function nextStep (arrayOfLinks) {
var promiseArray = [];
for(var i = 0; i < arrayOfLinks.length; i++){
promiseArray.push(requestPromise(arrayOfLinks[i]));
var promises = Promise.all(promiseArray);
console.log(promises);
}
return {arrayOfHtml: promises , arrayOfUrls: arrayOfLinks};
}
function lastStep (obj){
for(var i = 0; i < obj.arrayOfHtml.length; i++){
var $ = cheerio.load(obj.arrayOfHtml[i]);
//if page has a submit it must be a product page
if($('[type=submit]').length !== 0){
//add page to set
urlSet.add(obj.arrayOfUrls[i]);
console.log(obj.arrayOfUrls[i]);
} else if(remainder == undefined) {
//if not a product page, add it to remainder so it another scrape can be performed.
remainder = obj.arrayOfUrls[i];
console.log("remainder: " + remainder);
}
}
}
scrape(url)
.then(nextStep)
.then(lastStep)
.catch(function(err) {
// handle any error from any request here
console.log(err);
});
Run Code Online (Sandbox Code Playgroud)
有几件事你可以尝试。首先,在您的requestPromise函数中,调用“resolve()”时不需要返回,并且reject()。我不知道这是否会有什么不同,但你至少可以尝试一下。
接下来,正如评论中所讨论的,您应该更改拒绝和解决请求承诺的方式。最简单:
if(error) {
reject(error);
} else {
resolve(html);
}
Run Code Online (Sandbox Code Playgroud)
假设没有错误(只有 4xx 或 5xx 状态码才会发生错误),但状态码不是 200?您可以获得 2xx 或 3xx 范围内的任何内容,但不会出现错误,在这种情况下,您的问题requestPromise永远不会被解决或拒绝。这肯定会给你带来问题,因为所有的承诺都必须以其中之一结束。
下一期是在nextStep. 我将重构如下:
function nextStep (arrayOfLinks) {
var promiseArray = [];
for(var i = 0; i < arrayOfLinks.length; i++){
promiseArray.push(requestPromise(arrayOfLinks[i]));
}
return Promise.all(promiseArray)
.then(function (arrayOfHtml) {
return {arrayOfHtml: promises , arrayOfUrls: arrayOfLinks};
});
}
Run Code Online (Sandbox Code Playgroud)
使用 时Promise.all,您希望首先填充您的 Promise 数组,然后在完成所有异步调用后,再调用Promise.all(promisesArray). then末尾的额外部分all将获取从您的 Promise 数组中生成的 html,然后将其作为 Promise 与arrayOfLinksPromise 链中的下一步一起返回,在本例中是您的lastStep.
如果这些都不能解决你的问题,你需要回顾一下状态码问题,我之前遇到过问题,状态码是202,这意味着请求被接受,但是请求的处理没有完成。(您可以在此处阅读有关 HTTP 状态代码的更多信息)。这是一个非常相似的情况,我们有很多正在向其发出请求的网址。我们最终将所有返回 202 的 url 放回数组中tryAgain,然后再次尝试访问它们。
就您而言,您可以通过几种方式解决它。最简单的事情是拒绝除 200 之外的所有状态代码的承诺,这有点严格。您可以做的另一件事是,如果没有错误并且状态代码不是 200,那么您可以使用一些特殊值或简单的非 200 状态代码来解决承诺,这将表明您需要重试。然后nextStep,您可以过滤所有使用非 200 代码解析的结果,然后再次尝试点击它们。之后,您就可以完成lastStep. 如果您尝试了其他所有方法但都不起作用,我会尝试其中一种解决方案。不过这需要一些努力。
希望这可以帮助。如果您有任何疑问,请告诉我。