Как управлять «пулом» экземпляров PhantomJS - PullRequest
66 голосов
/ 01 апреля 2012

Я планирую собственный веб-сервис для внутреннего использования, который принимает один аргумент, URL и возвращает html, представляющий resolved DOM из этого URL. Под разрешением я подразумеваю, что веб-сервис сначала получает страницу по этому URL, затем использует PhantomJS для «рендеринга» страницы, а затем возвращает полученный источник после выполнения всех вызовов DHTML, AJAX и т. Д. Однако запуск фантома для каждого запроса (который я сейчас делаю) слишком медленный способ . Я бы предпочел иметь пул экземпляров PhantomJS, один из которых всегда доступен для обслуживания последнего вызова моей веб-службы.

Работали ли вы над подобными вещами раньше? Я предпочел бы основывать этот веб-сервис на работе других, чем писать для себя менеджер пула / http-прокси-сервер с нуля.

Больше контекста : я перечислил 2 похожих проекта, которые я видел до сих пор, и почему я избегал каждого из них, в результате чего возник вопрос об управлении пулом экземпляров PhantomJS.

jsdom - из того, что я видел, он имеет отличную функциональность для выполнения сценариев на странице, но он не пытается воспроизвести поведение браузера, поэтому, если бы я использовал его в качестве «общего средства распознавания DOM», в конечном итоге было много дополнительного кодирования для обработки всех видов случаев ребер, вызова событий и т. д. Первый пример, который я видел, - это необходимость вручную вызывать функцию onload () тега body для тестового приложения, которое я настроил с помощью node. Это казалось началом глубокой кроличьей норы.

Selenium - у него просто гораздо больше движущихся частей, поэтому настроить пул для управления долгоживущими экземплярами браузера будет гораздо сложнее, чем с помощью PhantomJS. Мне не нужны какие-либо преимущества макросов / сценариев. Мне просто нужен веб-сервис, который так же эффективен при получении веб-страницы и разрешении ее DOM, как если бы я просматривал этот URL с помощью браузера (или даже быстрее, если я могу заставить его игнорировать изображения и т. Д.)

Ответы [ 6 ]

62 голосов
/ 28 октября 2013

Я установил облачную службу PhantomJs, и она в значительной степени выполняет то, что вы просите. Это заняло у меня около 5 недель работы.

Самая большая проблема, с которой вы столкнетесь, - это известная проблема утечек памяти в PhantomJs . Способ, которым я работал вокруг этого, состоит в том, чтобы циклически повторять мои экземпляры каждые 50 вызовов.

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

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

Четвертая по величине проблема, с которой вам придется столкнуться, это взаимодействие между nodejs и phantomjs, к счастью, существует множество пакетов npm, которые решают эту проблему на выбор.

Так что я знаю, что я предвзят (поскольку я написал решение, которое я собираюсь предложить), но я предлагаю вам проверить PhantomJsCloud.com , который бесплатен для легкого использования .

Обновление в январе 2015 года: Еще одна (5-я?) Большая проблема, с которой я столкнулся, - это как отправить запрос / ответ от менеджера / балансировщика нагрузки. Первоначально я использовал встроенный HTTP-сервер PhantomJS, но продолжал сталкиваться с его ограничениями, особенно в отношении максимального размера ответа. Я закончил тем, что написал запрос / ответ в локальную файловую систему как линии связи. * Общее время, затрачиваемое на внедрение услуги, составляет, возможно, 20 человеко-недель, то есть, возможно, 1000 часов работы. * и к вашему сведению, я делаю полную переписку для следующей версии .... (в процессе)

17 голосов
/ 18 апреля 2012

Асинхронная библиотека JavaScript работает в Node и имеет функцию queue, которая очень удобна для такого рода вещей:

queue(worker, concurrency)

Создает объект очереди с указанным параллелизмом. Задачи, добавленные в очередь, будут обрабатываться параллельно (до лимита параллелизма). Если все рабочие выполняются, задача ставится в очередь до тех пор, пока она не станет доступной. Как только работник завершил задачу, вызывается обратный вызов задачи.

Какой-то псевдокод:

function getSourceViaPhantomJs(url, callback) {
  var resultingHtml = someMagicPhantomJsStuff(url);
  callback(null, resultingHtml);
}

var q = async.queue(function (task, callback) {
  // delegate to a function that should call callback when it's done
  // with (err, resultingHtml) as parameters
  getSourceViaPhantomJs(task.url, callback);
}, 5); // up to 5 PhantomJS calls at a time

app.get('/some/url', function(req, res) {
  q.push({url: params['url_to_scrape']}, function (err, results) {
    res.end(results);
  });
});

Ознакомьтесь с всей документацией для queue в файле readme .

проекта.
14 голосов
/ 02 декабря 2015

Для моей магистерской работы я разработал библиотеку phantomjs-pool , которая делает именно это.Это позволяет обеспечить рабочие места, которые затем отображаются на работников PhantomJS.Библиотека занимается распределением заданий, связью, обработкой ошибок, ведением журнала, перезапуском и некоторыми другими вещами.Библиотека успешно использовалась для сканирования более миллиона страниц.

Пример:

Следующий код выполняет поиск Google по номерам от 0 до 9 и сохраняет скриншотстраницы как googleX.png .Четыре веб-сайта сканируются параллельно (в связи с созданием четырех рабочих).Сценарий запускается через node master.js.

master.js (выполняется в среде Node.js)

var Pool = require('phantomjs-pool').Pool;

var pool = new Pool({ // create a pool
    numWorkers : 4,   // with 4 workers
    jobCallback : jobCallback,
    workerFile : __dirname + '/worker.js', // location of the worker file
    phantomjsBinary : __dirname + '/path/to/phantomjs_binary' // either provide the location of the binary or install phantomjs or phantomjs2 (via npm)
});
pool.start();

function jobCallback(job, worker, index) { // called to create a single job
    if (index < 10) { // index is count up for each job automatically
        job(index, function(err) { // create the job with index as data
            console.log('DONE: ' + index); // log that the job was done
        });
    } else {
        job(null); // no more jobs
    }
}

worker.js (работает в среде PhantomJS)

var webpage = require('webpage');

module.exports = function(data, done, worker) { // data provided by the master
    var page = webpage.create();

    // search for the given data (which contains the index number) and save a screenshot
    page.open('https://www.google.com/search?q=' + data, function() {
        page.render('google' + data + '.png');
        done(); // signal that the job was executed
    });

};
5 голосов
/ 25 марта 2015

В качестве альтернативы отличному ответу @JasonS вы можете попробовать PhearJS , который я построил.PhearJS - это супервизор, написанный на NodeJS для экземпляров PhantomJS, и предоставляет API через HTTP.Доступен с открытым исходным кодом от Github .

1 голос
/ 10 ноября 2015

если вы используете nodejs, почему бы не использовать selenium-webdriver

  1. запустить несколько экземпляров phantomjs как webdriver phantomjs --webdriver=port_number
  2. для каждого экземпляра phantomjs создать PhantomInstance

    function PhantomInstance(port) {
        this.port = port;
    }
    
    PhantomInstance.prototype.getDriver = function() {
        var self = this;
        var driver = new webdriver.Builder()
            .forBrowser('phantomjs')
            .usingServer('http://localhost:'+self.port)
            .build();
        return driver;
    }
    

    и поместите их все в один массив [phantomInstance1, phantomInstance2]

  3. создайте dispather.js, который получает бесплатный phantomInstance из массива и

    var driver = phantomInstance.getDriver();
    
0 голосов
/ 29 октября 2012

Если вы используете nodejs, вы можете использовать https://github.com/sgentle/phantomjs-node,, который позволит вам подключить произвольное число процессов phantomjs к вашему основному процессу NodeJS, следовательно, возможность использовать async.js и множество нод с положительными персонажами.

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