2014-11-26 4 views
1

Я написал небольшой скрипт в Node.js, чтобы очистить веб-страницу и получить некоторые ссылки. Часть скрапа выполняется с помощью Cheerio. Мой код здесь (упрощено для пространства):JSON document mysteriously "empties"

var request = require('request'); 
var cheerio = require('cheerio'); 

var base_url = 'http://www.naftemporiki.gr/finance/'; 

var mutuals = {}; 
mutuals.date = new Date(); 
mutuals.companies = []; 

var company = {}; 

request(base_url + 'mtfCompanies', function (error, response, html) { 
    if (!error && response.statusCode == 200) { 
     var $ = cheerio.load(html); 

     $('.blueRow.texttd.name a').each(function (i, element) { 
      var a = $(this); 

      company = {}; 
      company.name = a.text(); 
      company.link = a.attr('href'); 

      mutuals.companies.push(company); 
     }); 
    } 
    //console.log(mutuals);   // 1st place 
}); 
console.log(mutuals);    // 2nd place 

Здесь начинается самое интересное: Когда я пытаюсь вывести документ в формате JSON с «1-е место», внутри блока «запроса», то выходит красиво и правда. Примером здесь:

{ date: Wed Nov 26 2014 10:35:09 GMT+0200 (EET), 
    companies: 
    [ { name: ' J.P. MORGAN ASSET MANAGEMENT', 
     link: 'mtfCompany?id=J.P.+MORGAN+ASSET+MANAGEMENT' }, 
    { name: ' BNP PARIBAS INVESTMENT PARTNERS', 
     link: 'mtfCompany?id=BNP+PARIBAS+INVESTMENT+PARTNERS' }, 
    { name: ' PICTET', link: 'mtfCompany?id=PICTET' }, 
    { name: ' ALLIANZ ΑΕΔΑΚ', 
     link: 'mtfCompany?id=ALLIANZ+%ce%91%ce%95%ce%94%ce%91%ce%9a' }, 
    { name: ' ALLIANZ ΑΕΔΑΚ (ΑΝΤΙΠΡ.)', 
     link: 'mtfCompany?id=ALLIANZ+%ce%91%ce%95%ce%94%ce%91%ce%9a+(%ce%91%ce%9d%ce%a4%ce%99%ce%a0%ce%a1.)' }, 
    { name: ' ALLIANZ ΕΛΛΑΣ Α.Ε.', 
     link: 'mtfCompany?id=ALLIANZ+%ce%95%ce%9b%ce%9b%ce%91%ce%a3+%ce%91.%ce%95.' }]} 

При попытке вывода документа в формате JSON с «2-е место», вне любого блока и в конце исполнения, это то, что я получаю:

{ date: Wed Nov 26 2014 10:35:09 GMT+0200 (EET), companies: [] } 

Похоже, что массив «компаний» внутри документа JSON становится опустошенным. У меня есть подозрение, что «mutuals.companies = [];» по какой-то причине линия снова выполняется.

Может ли кто-нибудь помочь в этом?

UPDATE 1:

изменил код, как предложил использовать 'async.series ...'. Это обновленная версия:

var request = require('request'), 
    async = require('async'), 
    cheerio = require('cheerio'); 

var base_url = 'http://www.naftemporiki.gr/finance/'; 

var mutuals = {}; 
mutuals.date = new Date(); 
mutuals.companies = []; 

var company = {}; 

async.series([ 
    function(callback) { 
     request(base_url + 'mtfCompanies', function (error, response, html) { 
      if (!error && response.statusCode == 200) { 
       var $ = cheerio.load(html); 

       $('.blueRow.texttd.name a').each(function (i, element) { 
        var a = $(this); 

        company = {}; 
        company.name = a.text(); 
        company.link = a.attr('href'); 

        mutuals.companies.push(company); 
       }); 
      } 
     }); 
     callback(null, 'one'); 
    }, 
    function (callback) { 
     console.log(mutuals); 
     callback(null, 'two'); 
    } 
]); 

Все еще не работает. Тем не менее, выдается JSON:

{ date: Wed Nov 26 2014 10:35:09 GMT+0200 (EET), companies: [] } 

ответ

3

Ваше «2-е место» печатает переменную до завершения запроса.

Ваше «1-е место» работает, потому что оно находится внутри обратного вызова запроса. Запрос выполнен, данные вытягиваются, затем обратный вызов вызывается и успешно печатается.

Это способ работы асинхронного кода. Ничто не блокирует. Поэтому, когда вы выдаете свой запрос, узел сохраняет функцию обратного вызова, чтобы он мог выполнять код с результатами запроса.

Update 1:

Проблема с обновлением в основном то же самое. В вашей первой функции в серии, вызов callback вызывается до того, как запрос завершен. Если вы переместите обратный вызов в функцию, переданную для запроса, она будет вызвана после завершения запроса.

function(callback) { 
    request(base_url + 'mtfCompanies', function (error, response, html) { 
     if (!error && response.statusCode == 200) { 
      var $ = cheerio.load(html); 

      $('.blueRow.texttd.name a').each(function (i, element) { 
       var a = $(this); 

       company = {}; 
       company.name = a.text(); 
       company.link = a.attr('href'); 

       mutuals.companies.push(company); 
      }); 
      callback(null, 'one'); 
     } 
    }); 
}, 

Предложение 1

Развития в Node.js с обратными вызовами может оставить вас с глубокой вложенной структурой. Не допускайте, чтобы ваши утверждения if ухудшали вложенность. Используйте ранние возвращения вместо более глубокого гнездования. Пример:

function(callback) { 
    request(base_url + 'mtfCompanies', function (error, response, html) { 
     if(error) return callback(error); 
     if(response.statusCode !== 200) return callback('status code not 200'); 
     var $ = cheerio.load(html); 

     $('.blueRow.texttd.name a').each(function (i, element) { 
      var a = $(this); 

      company = {}; 
      company.name = a.text(); 
      company.link = a.attr('href'); 

      mutuals.companies.push(company); 
     }); 
     callback(null, 'one'); 
    }); 
}, 

Предложение 2

При использовании async может помочь упростить вещи с помощью именованных функций.Пример:

var request = require('request'), 
    async = require('async'), 
    cheerio = require('cheerio'); 

var base_url = 'http://www.naftemporiki.gr/finance/'; 

var mutuals = {}; 
mutuals.date = new Date(); 
mutuals.companies = []; 

var company = {}; 

function getPage(callback) { 
    request(base_url + 'mtfCompanies', function (error, response, html) { 
     if(error) return callback(error); 
     if(response.statusCode !== 200) return callback('status code not 200'); 
     var $ = cheerio.load(html); 

     $('.blueRow.texttd.name a').each(function (i, element) { 
      var a = $(this); 

      company = {}; 
      company.name = a.text(); 
      company.link = a.attr('href'); 

      mutuals.companies.push(company); 
     }); 
     callback(null, 'one'); 
    }); 
} 

function printMutuals(callback) { 
    console.log(mutuals); 
    callback(null, 'two'); 
} 

async.series([ 
    getPage, 
    printMutuals 
]); 
+0

Это из-за асинхронного характера node.js? Если да, как я могу это исправить? Я хотел бы иметь возможность выводить вне цикла, потому что я считаю, что он более «правилен» программно. – dsljanus

+1

Лучший способ управлять потоком асинхронного кода в настоящее время является модулем 'async'. Это определенно сложнее, чем синхронный код, однако взамен есть дополнительные возможности. Например, с node.js вы можете запросить несколько ресурсов параллельно и объединить их в ответ при завершении последнего. В синхронном коде вам необходимо каждый запрос каждого ресурса. Следующая версия node.js будет иметь функцию, называемую генераторами, которая сделает ваш асинхронный код более синхронным, но вам все равно нужно понять, что происходит под капотом. – Daniel

+0

Вы предлагаете использовать «async.series» для своей цели? – dsljanus

Смежные вопросы