PhantomJS — это отличный инструмент для тестирования ваших веб-приложений, и в целях парсинга данных с сайтов. В первом и во втором случае вам нужно правильно написать код, чтобы в конечном итоге завершить запущенный процесс PhantomJS. Если он не будет завершен, тогда он зависнет и будет нагружать сервер.
В этой записи я покажу вам способ проконтролировать ошибки и в случае их возникновения закрыть процесс PhantomJS.
Ни один из примеров на официальном сайте не пишут о том как это сделать, поэтому я решил написать об этом пост, чтобы ваш было проще разобраться с этим.
Есть два вариант решения проблемы, оба описаны ниже.
Как это выглядит
Я использую один дроплет за $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 не описали эту проблему в официальных доках, потому что я не единственный кто столкнулся этой проблемой. Надеюсь эта запись поможет вам решить эту проблему, если вы с ней столкнулись.