Почему я за l oop испортил все параметры? - PullRequest
0 голосов
/ 16 января 2020

Я пытаюсь проанализировать некоторые данные с нескольких веб-страниц, используя javascript. Я написал небольшой парсер для этой цели. Алгоритм выглядит следующим образом:

  1. Сначала откройте URL из моего файла .csv
  2. Найдите data Мне нужно на странице
  3. Сохранить URL и data в json файл

Мой код отлично выполняет 1. и 2., но иногда путается с номером 3. Вывод выглядит так:

URL 1 + data from URL 1  (correct line)
URL 2 + data from URL 2  (correct line)
URL 3 + data from URL 3  (correct line)
URL 4 + data from URL 4  (correct line)
URL 6(wrong URL) + data from another URL 
URL 5(wrong URL) + data from another URL
URL 7 + data from URL 7  (correct line)
URL 8 + data from URL 8  (correct line)
URL 9 + data from URL 9  (correct line)

Я предполагаю, что проблема в том, что некоторые страницы загружаются слишком долго, что портит весь процесс. Но я все еще не понимаю, почему это иногда сохраняет неправильные данные. Вот мой код:

var request = require('request');
var cheerio = require('cheerio');
var cloudscraper = require('cloudscraper');
var fs = require('fs');
var path = require('path');
var csvjson = require('csvjson');

//First, we read .csv file with our URL list
function getTheList() {
    urlList = fs.readFileSync(path.join(__dirname, 'data.csv'), { encoding : 'utf8'});
    var options = {
      delimiter : ';', // optional
      quote     : '"' // optional
     };
    urlList = csvjson.toObject(urlList, options);
    end = urlList.length;
    logs = [];

//here we start the loop reading and saving data from each url
  for (let p = 0; p < end; p += 1){
    grabTheData(urlList, p)
  }
} 

//this code extracts the data from the page and saves it to a json file
function grabTheData(urlList, p){
    setTimeout(function() { 

    url = url[p].ItemLink;
    cloudscraper.get(url, function(err, res, body){
        if (err) { 
            console.log(other.Time() + colors.yellow('Warn: ') + '- something went wrong with item ' + url);
            callback();

        } else {

            var $ = cheerio.load(body);
            /*
            here are the lines which extract the data I need
            dataIneed = ...;
            */

            logs.push({
                url, dataINeed
            });
            fs.writeFileSync('./logs.json', JSON.stringify(logs, null, 4));
        }
    });
//here I set a 2 seconds delay between each URL
    }, 2000 * p);
  }

getTheList()

1 Ответ

1 голос
/ 16 января 2020

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

Теперь есть очень быстрое решение для этого, просто измените область действия переменной url следующим образом:

function grabTheData(urlList, p){
    setTimeout(function() { 
        // Set scope of url variable to block
        let url = url[p].ItemLink;
        cloudscraper.get(url, function(err, res, body){
            if (err) { 
                console.log(other.Time() + colors.yellow('Warn: ') + '- something went wrong with item ' + url);
                callback();

            } else {

                var $ = cheerio.load(body);
                /*
                here are the lines which extract the data I need
                dataIneed = ...;
                */

                logs.push({
                    url, dataINeed
                });
                fs.writeFileSync('./logs.json', JSON.stringify(logs, null, 4));
            }
        });
//here I set a 2 seconds delay between each URL
    }, 2000 * p);
}

Это должно поддерживать ваши результаты в порядке.

Вот еще один (ИМХО, намного лучше) вариант, использующий обещания и избегающий использования setTimeout для разделения вызовов. Это должно избежать любого потенциального состояния гонки, так как вызов Promise.all сохранит порядок:

async function getTheList() {
    urlList = fs.readFileSync(path.join(__dirname, 'data.csv'), { encoding : 'utf8'});
    var options = {
        delimiter : ';', // optional
        quote     : '"' // optional
    };
    urlList = csvjson.toObject(urlList, options);
    let promiseList = urlList.map(urlEntry => grabTheDataUpdated(urlEntry.ItemLink));
    let logs = await Promise.all(promiseList);
    fs.writeFileSync('./new_logs.json', JSON.stringify(logs, null, 4));
}

// Promisified version of cloudscraper.get
function getCloudScraperData(url) {
    return new Promise((resolve, reject) => {
        cloudscraper.get(url, (err, res, body) => {
            if (err) {
                reject(err);
            } else {
                resolve ( { url, res, body });
            }
        })
    })
}

function getDataINeed(url, body) {
    // Use cheerio to process data..
    // Return mock data for now.. replace with actual data processed by cheerio..
    return `data from ${url}`;
}

async function grabTheDataUpdated(url) {
    try { 
        let result = await getCloudScraperData(url);
        let dataINeed = getDataINeed(result.url, result.body);
        return { url, dataINeed };
    } catch (error) { 
        return { url, dataINeed: "Error occurred: " + error.message };
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...