Как запустить open() внутри цикла PhantomJS?

Как запустить open() внутри цикла PhantomJS?

· JavaScript и Снипеты · 4 мин чтения

Во время работы с PhantomJS у вас не получиться использования метод open() внутри цикла. В этой записи я покажу почему эта проблема происходит и как её решить. 

Почему цикл не работает?

Давайте представим, что у вас массив ссылок на разные сайты и вы хотите зайти по каждой из них и скопировать все что есть на нем.

Ниже в этом посте я представлю полноценный рабочий пример для решения этой проблемы.

Если вы просто пройдетесь с помощью цикла, например for, то у вас ничего не получиться, потому что цикл не будет ждать пока завершиться запрос на сайт и сразу же пойдет на следующий круг и т.д.

Решение проблемы

Ниже представлено решение в виде рекурсивной функции recursiveRun.

Как она работает?

Я выделю самые важные моменты в коде, если у вас останутся вопросы — задавайте их в комментарии.

  1. На 20 строке мы запускаем 0 (нулевую, то есть первую) итерацию с позицией i, максимальное количество повторений взяли от длинны массива и третий аргумент — это сам массив.
  2. На 24-28 строке мы сравниваем если текущий индекс равен максимальному (длинне массива), то пишем в консоль «Конец» и закрываем текущую сессию PhantomJS. Это случиться когда скрипт пройдется по всему списку ссылок и скопирует с них данные, если они будут доступны.
  3. Если не удалось зайти по текущей ссылке, то пробуем зайти по следующей (строка 37)
  4. Записываем текст страницы в text на 42 строке и далее на 47 сохраняем текст в файл. Если в файле что-то уже есть, например текст с другого сайта, то он просто добавится к текущему.
  5. В конце переходим на следующий сайт (строка 49).
'use strict';
var page = require('webpage').create(),
    fs = require('fs');

var log = "[LOG ------->] ";


page.settings.loadImages = false; 
page.settings.resourceTimeout = 20000;
page.settings.userAgent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36';

// Массивы ссылок
var array = [
    "https://google.com",
    "https://yandex.ru",
    "http://bologer.ru",
    "https://bing.com"
];

recursiveRun(0, array.length - 1, array);

var recursiveRun = function(i, max, array) {

    if (i === max) {
        console.log(log + 'Конец');
        phantom.exit();
        return;
    }

    page.open(array[i], function (status) {

        page.viewportSize = { width: 1024, height: 900 };

 
        if (status !== "success") {
            console.log(log + "Ошибка, не удалось зайти на - " + array[i]);
            recursiveRun(i + 1, max, array);
        } else {

            // Дадим 5 сек для загрузки сайта
            setTimeout(function() {
                var text = page.evaluate(function() {
                    return document.body.innerText.trim();
                });

                // Записываем данные с сайта в текстовый файл
                fs.write("res.txt", "ССЫЛКА: " + array[i] + "\r\n\r\n" + text + "\r\n\r\n\r\n\r\n===============\r\n\r\n", 'a');

                recursiveRun(i + 1, max, array);
                console.log(log + "running next, index -> " + i);
                console.log(log + "======================================= ");
            }, 5000);
        }
    }); 
};

В консоли, во время выполнения скрипта вы должны увидеть что-то подобное (я это делал через Windows):

PhantomJS рекурсивная функция результат

Это означает, что i по позиции 0, 1 и 3 сработали. И четвертый тоже успешно прошел, только об этом не написало в консоли, потому что условие i == max сработало и остановило весь процесс рекурсивной функции.

В самом конце консоль вывела какие-то непонятные каракули, что это?

Это обычный вывод русских символов, так выглядит слово «Конец» в Windows консоли :D

Именно поэтому я написал некоторые console.log() сообщения на английском, чтобы хоть какой-то презентабельный вид был у вывода в консоль, ну и вам было понятнее смотря на скриншот.