Рубрики
Без рубрики

Масштабирование фантомых: принимая тысячи скриншотов полной страницы каждый день

Эта статья покажет вам, как использовать Phantomjs в масштабе, чтобы сделать несколько скриншотов веб-сайта в качестве спокойной службы.

Автор оригинала: Tony Schumacher.

Эта статья покажет вам, как использовать Phantomjs в масштабе, чтобы сделать несколько скриншотов веб-сайта в качестве спокойной службы. Я реализовал эту услугу по-своему, и есть много разных способов сделать это, но имейте в виду, что я говорю о примере реального жизни, который обслуживает 1000+ клиентов в день. Дело в использовании я говорю о том, это Custode , приложение SAAS на основе ползания, аналитики и скриншотов.

Проблемы создания полной страницы скриншоты

После строительства и запуска опекивания (вы можете прочитать о том, как я построил и запустил его на продукте Hunt Здесь ), я прошел проблему создания полнотечных скриншотов веб-сайта. При этом я столкнулся с тремя проблемами: Современные технологии, такие как Chrome Selenium Все еще есть проблемы с полной страницей скриншоты, не подделка их. Это означает, что Selenium примет несколько скриншотов, прокручиваясь и впоследствии выкладывая их в одно изображение. Это не будет работать для большинства веб-сайтов из-за фиксированных компонентов HTML. Пожилые технологии, такие как Phantomjs, а также новые, все довольно тяжелые на ресурсах CPU и RAM. Оптимизация его для запуска в масштабе и небольшие серверы было вполне важно для меня, чтобы сохранить расходы на серверы как можно ниже. Phantomjs багги и повторяется случайным образом время от времени. Это должно быть обработано.

Фантомии в моей задней части архитектуры

Потому что я влюбился в Node.js, я экспериментировал с большим количеством разных пакетов NPM, но ни один из них не сделал работу. Поэтому я решил просто использовать Фантомс обертка и построить функциональность поверх нее.

Код в этой статье представляет собой упрощенную версию моего заднего конца Custode, которая управляла несколькими серверами и ползает тысячи веб-сайтов в день – см. Диаграмму, представляющую архитектуру Toydee ниже.

Заметки

  • Сайт находится на переднем концевом сервере с Node.js и angularjs.
  • Там может быть еще много задних серверов, в зависимости от трафика (следовательно, + n ).
  • Это причина, по которой есть эльб AWS (балансировщик нагрузки) – он направляет трафик на задневские серверы, которые разделяют нагрузку между ними.
  • Приложение Node.js на фронт-концевом сервере имеет два целя:
    • Импорт и сохранение всех изображений в виде API для отдыха (изображения отправляются из задних серверов)
    • Нажатие изображений пользователей Premium в их Dropbox

(Если вы хотите больше читать об этом, вы можете обратиться к моему предыдущему посту.)

Для этого поста я снизил свою текущую резервующую реализацию, чтобы просто сделать полнотечные скриншоты, поэтому мои примеры не станут слишком сложными. Вы можете получить весь проект из моего [Github] ((https://github.com/tonyschu/) и запустить его на локальной машине. Я включил простой интерфейс для использования сервиса, но вы можете просто сделать сообщение запросы также.

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

После запуска приложения узла вы можете проверить его на localhost: 8089.

Обзор

Поскольку процесс создания нового экземпляра Phantomjs использует много ресурсов ЦП, я не мог просто запустить несколько браузеров для каждого процесса ползания. После тестирования различных подходов я знал, что эти аспекты должны были быть адресованы: Повторно используйте существующий экземпляр фантома как можно дольше, но закройте его, прежде чем он сбивает. Поскольку все работает в Async и Phantomjs, не могут сразу обращаться с несколькими операциями, все функции должны быть разработаны для обработки Async-функций (создайте вкладку браузера, откройте URL, извлеките HTML, представляют скриншоты и т. Д.) Ограничьте спину, чтобы запустить максимум четыре параллельных экземпляра. В противном случае сервер будет сбой. (Я проверял это на небольших случаях экземпляры EC2 на веб-сервисах Amazon. Если я хочу запустить более четырех экземпляров, я должен использовать более крупный сервер или масштабировать количество серверов.)

API

Это простое API для публикации ссылок на сайте и имя пользователя к приложению. На вашей местной машине конечная точка будет http://localhost: 8089/API/Phantom/: Пользователь Отказ Последняя часть URL ( : user ) будет использоваться для создания папки на вашем компьютере для хранения скриншотов.

   // API to post an array of websites to PhantomJS
    app.post('/api/phantom/:user', function (req, res) {
        //user or folder for the images
        var user = req.params.user;
        // array of websites from the post body
        var websites = req.body.websites;
        if(typeof user != "undefined" && typeof websites !="undefined"){
            // object for the our functions
            var crawlStatus = {index: 0, max: websites.length, user: user};
            // return true if successful
            var runPhantomJs = Crawler.startCrawler(websites, crawlStatus);
            // response for the http request
            var crawlAnswer;

            if (runPhantomJs == true) {
                crawlAnswer = "Start to make Screenshots of: " + websites;
            } else {
                crawlAnswer = "PhantomJS is too busy. :( Please try later";
            }
            res.send(crawlAnswer);
        }else{
            res.send("You need to define a user websites to crawl.");
        }
    });


Гусеничный

Здесь мы начнем с Crawlerobject , который будет использоваться для передачи данных между различными процессами. Он содержит текущую итерацию, URL-адреса веб-сайта, экземпляр Phantomjs и конфигурации. Поскольку фантомы довольно голодны для ресурсов, я ограничил приложение для проведения только четырехпроцессах параллельно. Вы можете изменить это в глобальной переменной «MaxiNStances», в зависимости от мощности вашей машины или сервера. Это должно хорошо работать на небольшом экземпляре EC2 на AWS.

// Requires
var phantom = require('phantom');
var fs = require('fs');
// global array of active PhantomJS instances
var phantomChildren = [];
var maxInstances = 4; //change this to run more PhantomJS instances in parallel
var maxIterations = 20; // the max of websites to run through a PhantomJS instance before creating a new one

// Object for crawling websites
function CrawlObject(index, websites, phantomInstance, crawlStatus) {
    return {
        index: index, // current index of the websites array
        websites: websites,
        processId: phantomInstance.process.pid, // process id of the child process
        crawlStatus: crawlStatus,
        phantomInstance: phantomInstance,
        resourceTimeout: 7000, // timeout to wait for phantom
        viewportSize: {width: 1024, height: 768}, // viewport of the PhantomJS browser
        format: {format: 'png', quality: '5'}, // format for the image
        timeOut: 5000 //Max time to wait for a website to load
    }
}

Создать экземпляр фантома

Эта функция создает новый свежий экземпляр фантома. Идентификатор его процесса будет храниться в глобальном массиве, поэтому мы всегда можем проверить, сколько экземпляров в данный момент работает или использует этот идентификатор, чтобы убить баггический экземпляр на нашем сервере. Недавно созданный CrawObject теперь передается в следующую функцию Createwebsitescreenshots Отказ Первое, что нужно проверить, является текущим индексом CRANDOBJECT. Это важно, потому что чем больше веб-сайтов, которые вы проходите через экземпляр фантома, тем более вероятно, что он является случайным образом сбоя.

Оптимизируя задний конец хранителя, я выяснил, что 20 итераций – это хороший предел для работы. После 20 итераций я выключу активный экземпляр и продолжаю процесс ползания со свежим, просто чтобы быть в безопасности. Если вы спросите себя – почему бы не просто использовать свежий экземпляр для каждого скриншота веб-сайта каждый раз – в основном потому, что процесс создания является основной причиной использования высокого использования CPU. Повторное использование экземпляра для рендеринга нескольких веб-сайтов очень важно при сохранении использования ресурсов сервера и в создании процесса скриншота быстрее.

//create browser instance
function initPhantom(websites, crawlStatus) {
    //only allow 4 instances at once
    if (checkPhantomStatus() == true)
        phantom.create(['--ignore-ssl-errors=no', '--load-images=true'], {logLevel: 'error'})
            .then(function (instance) {
                console.log("===================> PhantomJs instance: ", instance.process.pid);
                // store the process id in an array
                phantomChildren.push(instance.process.pid);
                var crawlObject = new CrawlObject(0, websites, instance, crawlStatus);
                createWebsiteScreenshots(crawlObject);
                return true;
            }).catch(function (e) {
            console.log('Error in initPhantom', e);
        });
    }
    return checkPhantomStatus();
}

Рендеринг скриншотов сайта

Вместо того, чтобы просто запустить нормальную петлю по этой функции, мы всегда ждем одной итерации, чтобы закончить, прежде чем позвонить следующему. Это необходимо по той же причине, причина, мы многократно используем экземпляр фантома. Это просто супер тяжелый на машине и не будет работать в масштабе. Как работает Phantomjs, заключается в том, что вы не можете выполнять операции параллельно, как принимать скриншоты, работая со содержанием HTML или даже нажатии и навигации на веб-сайте. (Да, вы можете делать входы, клики, загрузки файлов, но вам нужно подождать, пока каждый шаг не будет завершен, прежде чем начать следующий шаг.)

После настройки необходимых свойств и конфигураций мы можем позвонить Page.open , который создаст вкладку браузера с конкретным URL-адресом, который мы хотим ползти. Теперь мы можем выполнять операции, такие как получение содержимого HTML или создание полнотечных скриншотов. Файл PNG будет храниться в разделе /public/Изображения/Имя пользователя и может быть непосредственно вызывается с сервера, как на приложении index.html Отказ

// create a tab and make a screenshot
function createWebsiteScreenshots(crawl) {
    var website = crawl.websites[crawl.index];
    var user_folder = 'public/images/' + crawl.crawlStatus.user;
    var checkIterations = crawl.index >= maxIterations;
    var page;
    
    // if a PhantomJS instance is running for too long, it tends to crash sometimes
    // so start a fresh one
    if (checkIterations) {
        crawl.phantomInstance.exit();
        return restartPhantom(crawl);
    }

    crawl.phantomInstance.createPage()
        //open page in a tab
        .then(function (tab) {
            page = tab;
            page.property('viewportSize', crawl.viewportSize);
            page.setting("resourceTimeout", crawl.resourceTimeout);
            return page.open(website);
        })
        // get HTML content if you want to work with it
        .then(function () {
            // use a delay to make sure page is rendered properly
            return delay(crawl.timeOut).then(function () {
                return page.property('content');
            })
        })
        //render website to png file
        .then(function (content) {
            console.log("render %s / %s", crawl.index + 1, crawl.websites.length, "processId:", crawl.processId);
            var image = user_folder + "/" + new Date().toString() + "." + crawl.format.format;
            return page.render(image, crawl.format);
        })
        // close tabnd continue with loop
        .then(function () {
            page.close();
            continuePhantomLoop(crawl);
        })
        .catch(function (e) {
            restartPhantom(crawl, e);
        });
}

Любая ошибка будет поймана и обрабатываться с новым началом свежего экземпляра фантома. Это застраховало, что он не потерпит неудачу на конкретном сканировании сайта. Имейте в виду, что писал также некоторые функции помощника, такие как stremingmantomloop () Отказ Это не доставляется NPM Phantomjs, поэтому не стесняйтесь проверять весь код на Github.

Как масштабировать фантомые

Как уже упоминалось, приложение ограничено конфигурацией для работы только x количество экземпляров параллельно. То, как я масштабирую эту услугу для ползания тысяч веб-сайтов в день – это запустить его на нескольких небольших серверах параллельно. Таким образом, вызовы API пройдут через балансировщик нагрузки, который будет направлять и сбалансировать запросы на различные серверы, в зависимости от текущей нагрузки и количества пользователей. Я также автоматизировал серверы для загрузки и выключения в зависимости от объема запросов. В производстве изображения и данные затем выдвинуты на другой сервер, который можно вызвать с интерфейса. Изображения также выдвинуты на учетные записи Premium пользователей Dropbox.

Я пытался сохранить эту статью короткую, чтобы объяснить мою реализацию этой услуги простым и понятным способом. Однако, если у вас есть какие-либо вопросы, не стесняйтесь связаться со мной на http://ttonys.io или твиттер @Tonyschumaker Отказ

Я также рад ответить на любые вопросы в комментариях ниже. 👇.