回调函数Node JS

时间:2018-02-05 16:58:40

标签: javascript node.js nightmare

我最近潜入nodejs(并使用nightmare.js)来解析网站,并且遇到回调函数问题并显示返回的结果。我试图在另一个函数中调用一个单独的函数,但似乎无法返回任何结果。他们都返回undefined。非常感谢任何帮助。

 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)
    })

4 个答案:

答案 0 :(得分:1)

我不会打扰你告诉你,你不应该将回调与这样的承诺混在一起。但是现在让我们看看这个问题。

<案例1

你也检查错误怎么样?也许你的脚本会抛出错误。我可以看到你在.then上调用回调,但在.catch上没有。也许那时永远不会得到任何数据。

<案例2

让我们检查一下你的功能。您每次都在致电.end。您是否每次都在创建新的Nightmare实例?

getInventory功能上,您不应拨打.end。在getDetails功能上,您不应该致电.end。它结束了噩梦般的实例,你正在丢失你的数据。

完成所有功能后,请致电nightmare.end()。为了做到这一点,你需要了解更多关于Promises check case 3的信息。

案例3

了解承诺如何运作。在下面的行中,您永远不会等待该功能完成。

getINV = getDetails(newURL, function(returnString){
 callback(returnString)
})

你应该等待承诺完成。另外,请确保梦魇不会同时浏览两个链接。

所以继续学习Promises和async等待东西。

我如何解决您的代码?

我会使用Promise.all,.map和其他一些新东西。以下是为您完成的一些示例代码,不要直接复制粘贴或运行代码,尝试理解为什么它与您的代码不同以及它的结果可能是什么。

const pLimit = require("promise-limit")(2);

function getDetails(loadURL) {
  return nightmare
    .goto(loadURL)
    .wait(2000)
    .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(result => {
      let 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`;
      }
      return returnString;
    })
    .catch(error => {
      throw new Error(error);
    });
}

// We will need to get the total amount of pages that we need to parse
function getInventory(sURL) {
  return nightmare
    .goto(sURL)
    .wait(2000)
    .evaluate(() => {
      totals = [];
      items = {};
      totalCars = $(".total-found .count").text();
      carsOnPage = $(".hidden-xs .used-vehicle").size();
      items["carTotal"] = totalCars;
      items["onPage"] = carsOnPage;
      const pageCalc = totalCars / carsOnPage;
      items["tPages"] = Math.ceil(pageCalc);
      totals.push(items);
      return totals;
    })
    .then(result => {
      let totalCars = "";
      let totalPages = "";
      for (item in result) {
        totalPages = result[item].tPages;
        totalCars = result[item].carTotal;
      }
      counter = 0;
      newURL = "";
      urls = [];
      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`;
        }
        // push to the url array
        // use .map for cleaner code
        urls.push(newURL);
      }
      // return a new promise with concurrency limit
      return Promise.all(
        urls.map(url => {
          return limit(() => getDetails(newURL));
        })
      );
    })
    .catch(error => {
      throw new Error(error);
    });
}

getInventory(startURL)
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.err(error);
  });

资源:

答案 1 :(得分:0)

尝试使用回调而不是返回

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)
    })

答案 2 :(得分:0)

也许以下内容可行,更改循环以减少和映射,取出jQuery并进行一些小的更改。

最重要的是:

  1. 从html元素获取属性会返回一个字符串,您应该将其转换为数字。
  2. 摆脱回调并让函数返回promises。
  3. 以下是代码:

    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)
    );
    

答案 3 :(得分:0)

学习具有异步功能的承诺和回调是一项艰巨的任务。我能够通过以下方式实现此目的。谢谢大家的帮助和指导。每个答案都把我送到了另一个兔子洞,最终找到了解决办法。

    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)
        }
    });