Javascript 回调函数节点JS
我最近潜入nodejs,使用噩梦.js解析一个网站,在回调函数和显示返回结果方面遇到了问题。我试图在另一个函数中调用一个单独的函数,但似乎无法返回任何结果。它们都返回未定义。在此方面的任何帮助都将不胜感激Javascript 回调函数节点JS,javascript,node.js,nightmare,Javascript,Node.js,Nightmare,我最近潜入nodejs,使用噩梦.js解析一个网站,在回调函数和显示返回结果方面遇到了问题。我试图在另一个函数中调用一个单独的函数,但似乎无法返回任何结果。它们都返回未定义。在此方面的任何帮助都将不胜感激 function getDetails(loadURL, callback){ nightmare.goto(loadURL) .wait(2000) .evaluate(function(){ var gigs = [];
function getDetails(loadURL, callback){
nightmare.goto(loadURL)
.wait(2000)
.evaluate(function(){
var gigs = [];
$('.hidden-xs .used-vehicle').each(function(){
item = {}
item["year"] = $(this).attr('data-year')
item["make"] = $(this).attr('data-make')
item["model"] = $(this).attr('data-model')
item["body"] = $(this).attr('data-body')
item["color"] = $(this).attr('data-ext-color')
item["trim"] = $(this).attr('data-trim')
item["mileage"] = $(this).attr('data-mileage')
item["transmission"] = $(this).attr('data-transmission')
item["vin"] = $(this).find(".vehicle-overview").attr('id')
item["title"] = $(this).find(".vehicle-overview h2 a").text()
item["link"] = $(this).find(".vehicle-overview h2 a").attr('href')
item["price"] = $(this).find(".vehicle-content .price").text()
gigs.push(item)
})
return gigs
})
.end()
.then(function(result){
var returnString = '';
for(gig in result){
returnString = returnString + result[gig].title + " " + result[gig].link + " " + result[gig].year + " " + result[gig].make + " " + result[gig].model + " " + result[gig].body + " " + result[gig].color + " " + result[gig].trim + " " + result[gig].transmission + " " + result[gig].vin + " " + result[gig].price + "\n"
}
callback(returnString)
})
}
// We will need to get the total amount of pages that we need to parse
function getInventory(sURL, callback){
nightmare.goto(sURL)
.wait(2000)
.evaluate(function(){
totals = [];
items = {}
totalCars = $('.total-found .count').text()
carsOnPage = $('.hidden-xs .used-vehicle').size()
items['carTotal'] = totalCars
items['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
items['tPages'] = Math.ceil(pageCalc)
totals.push(items)
return totals
})
.end()
.then(function(result){
var totalCars = '';
var totalPages = '';
for (item in result){
totalPages = result[item].tPages
totalCars = result[item].carTotal
}
counter = 0;
newURL = '';
returnDetails = '';
for (i =0; i < totalPages; i++){
if (i == 0){
newURL = sURL;
} else {
counter = i + 1;
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
//console.log(newURL)
getINV = getDetails(newURL, function(returnString){
callback(returnString)
})
returnDetails = returnDetails + getINV
}
callback(returnDetails)
})
}
getInventory(startURL, function(result){
console.log(result)
})
尝试使用回调而不是返回
function getDetails(loadURL, callback){
nightmare.goto(loadURL)
.wait(2000)
.evaluate(function(callback){
var gigs = [];
$('.hidden-xs .used-vehicle').each(function(){
item = {}
item["year"] = $(this).attr('data-year')
item["make"] = $(this).attr('data-make')
item["model"] = $(this).attr('data-model')
item["body"] = $(this).attr('data-body')
item["color"] = $(this).attr('data-ext-color')
item["trim"] = $(this).attr('data-trim')
item["mileage"] = $(this).attr('data-mileage')
item["transmission"] = $(this).attr('data-transmission')
item["vin"] = $(this).find(".vehicle-overview").attr('id')
item["title"] = $(this).find(".vehicle-overview h2 a").text()
item["link"] = $(this).find(".vehicle-overview h2 a").attr('href')
item["price"] = $(this).find(".vehicle-content .price").text()
gigs.push(item)
})
callback(gigs)
})
.end()
.then(function(result){
var returnString = '';
for(gig in result){
returnString = returnString + result[gig].title + " " + result[gig].link + " " + result[gig].year + " " + result[gig].make + " " + result[gig].model + " " + result[gig].body + " " + result[gig].color + " " + result[gig].trim + " " + result[gig].transmission + " " + result[gig].vin + " " + result[gig].price + "\n"
}
callback(returnString)
})
}
// We will need to get the total amount of pages that we need to parse
function getInventory(sURL, callback){
nightmare.goto(sURL)
.wait(2000)
.evaluate(function(){
totals = [];
items = {}
totalCars = $('.total-found .count').text()
carsOnPage = $('.hidden-xs .used-vehicle').size()
items['carTotal'] = totalCars
items['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
items['tPages'] = Math.ceil(pageCalc)
totals.push(items)
return totals
})
.end()
.then(function(result){
var totalCars = '';
var totalPages = '';
for (item in result){
totalPages = result[item].tPages
totalCars = result[item].carTotal
}
counter = 0;
newURL = '';
returnDetails = '';
for (i =0; i < totalPages; i++){
if (i == 0){
newURL = sURL;
} else {
counter = i + 1;
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
//console.log(newURL)
getINV = getDetails(newURL, function(returnString){
callback(returnString)
})
returnDetails = returnDetails + getINV
}
callback(returnDetails)
})
}
getInventory(startURL, function(result){
console.log(result)
})
也许下面的方法会起作用,将循环更改为reduce和map,去掉jQuery并进行一些小的更改 最重要的是: 从html元素获取属性会返回一个字符串,您应该将其转换为数字。 摆脱回调,让函数返回承诺。 代码如下:
const getDetails = loadURL =>
nightmare.goto(loadURL)//return promise here
.wait(2000)
.evaluate(
()=>
Array.from(document.querySelectorAll('.hidden-xs .used-vehicle'))
.reduce(
(all,item)=>
all.concat(
[
element.getAttribute('data-year'),
element.getAttribute('data-make'),
element.getAttribute('data-model'),
element.getAttribute('data-body'),
element.getAttribute('data-ext-color'),
element.getAttribute('data-trim'),
element.getAttribute('data-mileage'),
element.getAttribute('data-transmission'),
element.querySelector(".vehicle-overview").getAttribute('id'),
element.querySelector(".vehicle-overview h2 a").innerText,
element.querySelector(".vehicle-overview h2 a").getAttribute('href'),
element.querySelector(".vehicle-content .price").innerText
].join(" ")
),
[]//the all array
)
);
// We will need to get the total amount of pages that we need to parse
const getInventory = sURL =>
nightmare.goto(sURL)
.wait(2000)
.evaluate(
()=> {
//there is only one item here, not sure why you push it into totals
// and call it items
const item = {}
//getAttribute returns a string, parse it to number
totalCars = parseInt(document.querySelector('.total-found .count').innerText,10);
carsOnPage = document.querySelectorAll('.hidden-xs .used-vehicle').length;
item['carTotal'] = totalCars
item['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
item['tPages'] = Math.ceil(pageCalc)
return item;
}
)
.then(
totalItem =>{
var totalCars = '';
var totalPages = '';
totalPages = totalItem.tPages
totalCars = totalItem.carTotal
newURL = '';
returnDetails = '';
return Array.from(new Array(totalPages),(_,index)=>index+1)
.reduce(
(p,counter)=>
p.then(
results=>{
if (counter === 1) {
newURL = sURL;
} else {
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
return getDetails(newURL)
.then(
result=>results.concat(result)
);
}
),
Promise.resolve([])
);
}
);
getInventory(startURL)
.then(
result=>
console.log(result)
).catch(
err=>
console.warn("Something went wrong:",err)
);
我不想麻烦告诉你,你不应该把回电和这样的承诺混为一谈。但现在让我们看看问题所在 案例1 你也检查一下错误,怎么样?也许你的脚本出错了。我可以看到你正在打电话回叫。然后什么也没打。接住。也许那时他从未得到任何数据 案例2 让我们检查一下你的函数。你在呼叫。每次都结束。你每次也在创建新的噩梦实例吗 在getInventory函数中,不应调用.end。在getDetails函数中,不应调用.end。它正在结束噩梦般的实例,而您正在丢失数据 完成所有函数和工作后,调用噩梦.end。要正确地做到这一点,您需要了解下面的承诺检查案例3的更多信息 案例3 了解承诺是如何运作的。在下面的一行中,您永远不会等待函数完成
getINV = getDetails(newURL, function(returnString){
callback(returnString)
})
你应该等待承诺完成。另外,请确保噩梦不会试图同时浏览两个链接
因此,请继续学习承诺和异步等待的内容
我将如何解决您的代码
我会使用Promise.all、.map和其他一些新东西。下面是一些为您编写的示例代码,不要复制粘贴或直接运行代码,请尝试理解为什么它与您的代码不同,以及它会产生什么结果
常数pLimit=requirepromise-limit2;
函数getDetailsloadURL{
回归噩梦
戈托拉杜尔先生
wait2000先生
.evaluate=>{
常量gigs=[];
$.hidden xs.used-vehicle.eachfunction{
项目={};
项目[年度]=$this.attrdata-year;
item[make]=$this.attrdata-make;
项目[模型]=$this.attrdata-model;
item[body]=$this.attrdata-body;
项目[颜色]=$this.attrdat-ext-color;
item[trim]=$this.attrdata-trim;
项目[里程]=$this.attrdata-里程;
项目[传输]=$this.attr数据传输;
项目[vin]=$this
.查找.车辆概述
.attrid;
项目[标题]=$this
.查找.车辆概述h2 a
文本
项目[链接]=$this
.查找.车辆概述h2 a
.attrref;
项目[价格]=$this
.find.vehicle-content.price
文本
gigs.pushitem;
};
返回演出;
}
.thenresult=>{
让returnString=;
在结果中{
返回字符串=
`${returnString+
结果[gig]。标题}${result[gig]。链接}${result[gig]。年份}${result[gig]。制作}${result[gig]。模型}${result[gig]。正文}${result[gig]。颜色}${result[gig]。修剪}${result[gig]。传输}${result[gig]。vin}${result[gig]。价格}\n`;
}
返回字符串;
}
.catcherror=>{
抛出新的错误;
};
}
//我们需要得到需要解析的页面总数
函数getInventorysURL{
回归噩梦
gotosURL先生
wait2000先生
.evaluate=>{
总数=[];
项目={};
totalCars=$.total found.count.text;
carsOnPage=$.hidden xs.used-vehicle.size;
项目[carTotal]=车辆总数;
项目[第页]=汽车第页;
const pageCalc=总汽车/汽车页面;
项目[tPages]=Math.ceilpageCalc;
总计6个项目;
返回总数;
}
.thenresult=>{
让我们来看看汽车=;
设totalPages=;
对于结果中的项目{
totalPages=结果[项目].tPages;
totalCars=结果[项目].carTotal;
}
计数器=0;
新网址=;
URL=[];
returnDetails=[];
对于i=0;i}; 学习异步函数的承诺和回调是一项相当艰巨的任务。我能够让这项工作与以下。谢谢你们的帮助和指导。每个答案都让我陷入了一个不同的困境,最终找到了解决办法
function getInventory(sURL){
nightmare.goto(sURL)
.wait(2000)
.evaluate(function(){
totals = [];
items = {}
totalCars = $('.total-found .count').text()
carsOnPage = $('.hidden-xs .used-vehicle').size()
items['carTotal'] = totalCars
items['onPage'] = carsOnPage
var pageCalc = (totalCars / carsOnPage)
items['tPages'] = Math.ceil(pageCalc)
totals.push(items)
return totals
})
.then(result => {
var totalCars = '';
var totalPages = '';
for (item in result){
totalPages = result[item].tPages
totalCars = result[item].carTotal
}
counter = 0;
let links = [];
let returnLinks = '';
newURL = '';
for (i = 0; i < totalPages; i++){
if (i == 0){
newURL = sURL;
} else {
counter = i + 1;
newURL = sURL + "#action=im_ajax_call&perform=get_results&_post_id=5&page=" + counter + "&show_all_filters=false";
}
links.push(newURL);
}
return links;
})
.then(results => {
var arrayLinks = results;
arrayLinks.reduce(function(accumulator, url){
return accumulator.then(function(newResults){
return nightmare.goto(url)
.wait(5000)
.evaluate(() => {
const gigs = [];
$(".hidden-xs .used-vehicle").each(function() {
item = {};
item["year"] = $(this).attr("data-year");
item["make"] = $(this).attr("data-make");
item["model"] = $(this).attr("data-model");
item["body"] = $(this).attr("data-body");
item["color"] = $(this).attr("data-ext-color");
item["trim"] = $(this).attr("data-trim");
item["mileage"] = $(this).attr("data-mileage");
item["transmission"] = $(this).attr("data-transmission");
item["vin"] = $(this).find(".vehicle-overview").attr("id");
item["title"] = $(this).find(".vehicle-overview h2 a").text();
item["link"] = $(this).find(".vehicle-overview h2 a").attr("href");
item["price"] = $(this).find(".vehicle-content .price").text();
gigs.push(item);
});
return gigs;
})
.then(detail => {
for (gig in detail) {
try {
var carVin = detail[gig].vin;
var carTitle = detail[gig].title;
var carDescrip = detail[gig].year + " " + detail[gig].make + " " + detail[gig].model;
var carURL = detail[gig].link;
var carMake = detail[gig].make;
var carModel = detail[gig].model;
var carYear = detail[gig].year;
var carMileageFull = detail[gig].mileage;
var carMileage = carMileageFull.replace(',', '');
var carTransmission = detail[gig].transmission;
var carBody = detail[gig].body;
var carPriceFull = detail[gig].price;
var carPriceFull = carPriceFull.replace('$', '');
var carPriceFull = carPriceFull.replace('*', '');
var carPriceFull = carPriceFull.replace(',', '');
var carPrice = carPriceFull.trim();
var dealerAddress = "{addr1: '"+ addressFull.addr1 +"', city: '"+ addressFull.city +"', region: '"+ addressFull.region +"', postal_code: '"+ addressFull.postal_code +"', country: '"+ addressFull.country +"'}";
var dealerLat = latLongFull.latitude;
var dealerLong = latLongFull.longitude;
var carColor = detail[gig].color;
arrSetup = [carVin, carTitle, carDescrip, carURL, carMake, carModel, carYear, carMileage, 'MI', '', '', 'AUTOMATIC', 'GASOLINE', 'OTHER', 'Other', carVin, 'OTHER', carPrice + " USD", dealerAddress, carColor, carPrice + " USD", 'AVAILABLE', 'USED', dealerLat, dealerLong];
newResults.push(arrSetup);
}
catch(error){
returnString += error;
}
}
return newResults;
})
.catch(error => {
throw new Error(error);
});
});
}, Promise.resolve([]))
.then(function(finalCall){
/*
We need to get the 3rd image on every vdp in the array. We will need to create a loop, go to the page, get the image and properly insert it into the proper array index
*/
finalCall.reduce(function(accumulator, resultArray){
return accumulator.then(function(finalResults){
var vdp = resultArray[3];
return nightmare.goto(vdp)
.wait(500)
.evaluate(() => {
var thirdIMG = $('.gallery-thumbs .owl-item:nth-of-type(3) img').attr('src');
return thirdIMG;
})
.then(imgResult => {
// 9
resultArray.splice(9, 1, imgResult);
console.log(resultArray);
finalResults.push(resultArray);
return finalResults;
})
.catch(error => {
throw new Error(error);
});
});
}, Promise.resolve([]))
.then(finalInsert => {
const csvWriter = createCsvWriter({
header: ["vehicle_id", "title", "description", "url", "make", "model", "year", "mileage.value", "mileage.unit", "image[0].url", "image[0].tag[0]", "transmission", "fuel_type", "body_style", "drivetrain", "vin", "condition", "price", "address", "exterior_color", "sale_price", "availability", "state_of_vehicle", "latitude", "longitude"],
path: 'test.csv'
});
var records = finalInsert;
console.log(records)
csvWriter.writeRecords(records)
.then(() => {
nightmare.end();
console.log('...Done');
});
})
});
})
.catch(function(error){
return error;
})
}
getInventory(startURL, function(response){
try {
console.log("This is the response" + response);
}
catch(error){
console.log(error)
}
});
不接受回调参数。只要归还承诺。@Bergi如果我理解正确的话,因为回调现在有点痛苦,你建议删除这些,只需要一个简单的返回语句。我试过了,但还是不行。这里很混乱。一个正确的返回语句,但是:-我想说的是,你不应该把回调风格和承诺风格混为一谈。不过,您可能应该先学习回调。感谢您的响应,但是我仍然会为循环中的每个getDetails调用返回未定义的结果。感谢您的响应,但似乎仍然返回未定义的结果。我从element.getAttribute改回使用jquery,因为第一次迭代导致未定义元素的错误。我还在努力。如果我解决了这个问题,我也会留下一个答案,以防这对将来的任何人都有帮助。@soulglow1985为什么要使用噩梦?是否要刮取在客户端上呈现react、angular、vue。。。?如果是这样,那么最好使用http请求并向数据api发出请求。如果站点提供html,您可以将http请求与jsdom或类似scrapy的东西一起使用,并行解析html。我认为问题在于调用了getDetails,而之前的getDetails尚未完成。已更新应答以连续调用getDetails。