Проблема с timeout'ом PhantomJS запущенным с PHP

Проблема с timeout’ом PhantomJS запущенным с PHP

· JavaScript, Блог и Парсинг · 3 мин чтения

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

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

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

Есть два вариант решения проблемы, оба описаны ниже.

Как это выглядит

DigitalOcean CPU

Я использую один дроплет за $5 на DigitalOcean. За это мне дают 512МБ памяти, 1-коровый процессор и 20МБ SSD диска. Достаточно не плохо, если вам нужно сделать что-то не особо громоздкое (например, парсить в небольшой количестве).

PhantomJS после того как зависал съедал у меня около 318% памяти, что в два раза превышает то, что у меня есть и тем самым сервер зависал и больше не отвечал (даже при подключении по SSH). Единственное, что мне оставалось сделать — это перезагрузить его через панель управления DigitalOcean.

О коде ниже

В двух примерах кода ниже будут строки с «…» — это я урезал ненужную часть кода, чтобы не забивать вам голову лишней информацией.

Как я использовал скрипт

Я создавал php файл внутри которого было что-то похожее на это:

<?php 

exec('phantomjs script.js')

?>

Где script.js — это PhantomJS. И если script.js получает какую-либо ошибку до того как срабатывает phantom.exit(), то PhantomJS начинает кушать всю память сервера и тем самым он перестает работать (что не очень хорошо, если у вас на нем есть какие-то другие важные процессы).

Ниже описано как эту проблему можно решить.

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

Вам нужно обернуть весь код в try… catch конструкцию. Все что вы напишите внутри try будет выполняться до того момента пока вы не получите какую-либо ошибку внутри него и если такое случиться, то сработает catch.

Тем самым нам нужно занести почти весь код PhantomJS в try и в catch добавить закрытие сессии (phantom.exit()).

Допустим, мы возьмем мой пример скрипта из Парсинг Яндекс новостей с помощью Phantomjs. Правильный вариант этого скрипта будет выглядеть следующим образом:

'use strict';
var page = require('webpage').create();

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


try {
    page.open('https://yandex.ru/', function (status) {
        page.viewportSize = { width: 1024, height: 900 };

        page.injectJs('http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js');

        ...
    });
} catch(e) {
    console.log('Ошибка работы PhantomJS.');
    console.log('Сообщение ошибки:');
    console.log(e);
    phantom.exit();
}

page.onInitialized = function(){
    ...
};

Добавленные строки отмечены в коде выше.

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

Использовать callback page.OnError, который работает на подобии try...catch, только им не нужно оборачивать код.

Я возьму код из той же записи, что и первое решение и сделаю исправленный вариант:

'use strict';
var page = require('webpage').create();


phantom.onError = function(msg, trace) {
  var msgStack = ['PHANTOM ERROR: ' + msg];
  if (trace && trace.length) {
    msgStack.push('TRACE:');
    trace.forEach(function(t) {
      msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
    });
  }
  console.error(msgStack.join('\n'));
  phantom.exit(1);
};


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


page.open('https://yandex.ru/', function (status) {
    page.viewportSize = { width: 1024, height: 900 };

    page.injectJs('http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js');

    ...
});


page.onInitialized = function(){
    ...
};

Добавленные строки отмечены в коде выше.

phantom.onError сработает в том случае когда произойдет какая-либо ошибка и тем самым завершит процесс.

Вывод

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