Статьи

Безголовый WebKit и PhantomJS

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

Созданный на основе WebKit , движка Chrome и Safari, PhantomJS дает вам массу возможностей браузера без тяжелого графического интерфейса. Начать работу с PhantomJS легко — просто загрузите исполняемый файл . Затем создайте файл с именем hello.js и добавьте следующие строки.

 console.log("Hello World!"); phantom.exit(); 

Чтобы выполнить скрипт, запустите команду, показанную ниже. Обратите внимание, что исполняемый файл phantomjs должен находиться либо в вашем текущем каталоге, либо где-нибудь в PATH вашей среды. Если все настроено правильно, PhantomJS напечатает Hello World! в консоль, а затем завершите работу при phantom.exit() .

 phantomjs hello.js

Работа с веб-страницами

После запуска и запуска PhantomJS вы можете начать автоматизацию Интернета. В следующем примере загружается домашняя страница Google, а затем сохраняется снимок экрана в файл. Строка 1 создает новый экземпляр веб-страницы. В строке 4 веб-страница загружает google.com . После завершения загрузки страницы выполняется функция обратного вызова onLoadFinished() . Обратный вызов получает единственный аргумент, status , который указывает, была ли страница загружена успешно или нет. URL загруженной страницы доступен в page.url . Это свойство может быть особенно полезно, когда страницы содержат перенаправления, и вы хотите точно знать, где вы оказались. Снимок экрана сделан в строке 8 с использованием метода render() страницы. render() может создавать файлы PNG, GIF, JPEG и PDF.

 var page = require("webpage").create(); var homePage = "http://www.google.com/"; page.open(homePage); page.onLoadFinished = function(status) { var url = page.url; console.log("Status: " + status); console.log("Loaded: " + url); page.render("google.png"); phantom.exit(); }; 

Настройки страницы

Объекты страницы имеют ряд настроек, которые можно настраивать в зависимости от потребностей вашего приложения. Например, если вы заинтересованы только в загрузке исходного кода, вы можете ускорить работу приложения, игнорируя файлы изображений и отключая JavaScript. Предыдущий пример переписан ниже, чтобы отразить эти изменения. Измененные настройки отображаются в строках 3 и 4. Обратите внимание, что любые изменения настроек должны иметь место до вызова open() . Если вы посмотрите на скриншот из этого примера, вы заметите, что изображение логотипа Google отсутствует, но остальная часть страницы работает.

 var page = require("webpage").create(); var homePage = "http://www.google.com/"; page.settings.javascriptEnabled = false; page.settings.loadImages = false; page.open(homePage); page.onLoadFinished = function(status) { var url = page.url; console.log("Status: " + status); console.log("Loaded: " + url); page.render("google.png"); phantom.exit(); }; 

Доступ к файловой системе

До сих пор наши примеры загружали страницы и сохраняли снимки экрана в виде файлов изображений. Хотя это, несомненно, круто, многие приложения предпочитают хранить исходный код в файловой системе. PhantomJS делает это возможным, предоставляя обширный API файловой системы. В следующем примере модуль FileSystem используется для записи исходного кода google.com в файл. Сначала модуль FileSystem импортируется в строке 2. В строке 6 выходной файл открывается для записи. В строке 7 данные записываются в файл с помощью метода write() . Фактический исходный код доступен через свойство content страницы. Наконец, файл закрывается и PhantomJS завершается.

 var page = require("webpage").create(); var fs = require("fs"); var homePage = "http://www.google.com/"; page.open(homePage); page.onLoadFinished = function(status) { var file = fs.open("output.htm", "w"); file.write(page.content); file.close(); phantom.exit(); }; 

Выполнение JavaScript

Одной из самых мощных функций PhantomJS является возможность взаимодействия со страницей через JavaScript. Это позволяет очень легко автоматизировать такие задачи, как нажатие кнопок и отправка форм. Наш следующий пример выполняет поиск в Интернете, загружая домашнюю страницу Google, вводя запрос, а затем отправляя форму поиска. Начало примера должно выглядеть знакомо. Новый материал начинается со строки 8, где мы определяем, какая страница была загружена. Если это домашняя страница, вызывается метод evaluate() страницы evaluate() . evaluate() выполняет код, который вы предоставляете в контексте страницы. По сути, это дает вам ту же силу, что и оригинальный разработчик страницы. Как это круто?

 var page = require("webpage").create(); var homePage = "http://www.google.com/"; page.open(homePage); page.onLoadFinished = function(status) { var url = page.url; console.log("Status: " + status); console.log("Loaded: " + url); if (url === homePage) { page.evaluate(function() { var searchBox = document.querySelector(".lst"); var searchForm = document.querySelector("form"); searchBox.value = "JSPro"; searchForm.submit(); }); } else { page.render("results.png"); phantom.exit(); } }; 

Внутри evaluate() , мы находим поле поиска и форму. Мы устанавливаем значение в поле поиска «JSPro», а затем отправляем форму. Это приведет к onLoadFinished() метода onLoadFinished() страницы. Однако на этот раз снимок экрана с результатами поиска, и PhantomJS завершает работу.

PhantomJS также предоставляет два метода, includeJs() и injectJs() , которые позволяют добавлять внешние файлы сценариев на страницу. includeJs() используется для включения любого файла скрипта, доступного со страницы. Например, вы можете включить jQuery в нашем предыдущем примере, используя следующий код. Обратите внимание на вызов includeJs() в строке 9, а также на синтаксис jQuery внутри evaluate() .

 var page = require("webpage").create(); var homePage = "http://www.google.com/"; page.open(homePage); page.onLoadFinished = function(status) { var url = page.url; console.log("Status: " + status); console.log("Loaded: " + url); if (url === homePage) { page.includeJs("http://code.jquery.com/jquery-1.8.3.min.js", function() { console.log("Loaded jQuery!"); page.evaluate(function() { var searchBox = $(".lst"); var searchForm = $("form"); searchBox.val("JSPro"); searchForm.submit(); }); }); } else { page.render("results.png"); phantom.exit(); } }; 

Метод injectJs() похож на includeJs() . Разница в том, что внедренный файл сценария не должен быть доступен из объекта страницы. Это позволяет, например, вводить скрипты из локальной файловой системы.

PhantomJS и Node.js

К сожалению, PhantomJS не особенно хорошо интегрируется с Node.js. Было создано несколько проектов, которые пытаются управлять PhantomJS из Node.js, но все они являются чем-то вроде клуджа. Существующие проекты используют дочерний модуль процесса для порождения экземпляров PhantomJS. Затем PhantomJS загружает специальную веб-страницу, которая использует WebSockets для связи с Node.js. Это может быть не идеально, но это работает.

Два наиболее популярных модуля PhantomJS Node — это node-phantom и phantomjs-node . Недавно я начал работать над своим собственным модулем PhantomJS Node с именем ghostbuster . Ghostbuster похож на фантом узла, но пытается уменьшить вложенность обратного вызова, предоставляя более мощные команды. Меньшее количество обращений к PhantomJS также означает, что при общении через WebSockets тратится меньше времени. Другой альтернативой является zombie.js , легкий браузер без головы, построенный на основе jsdom . Zombie не такой мощный, как PhantomJS, но это настоящий модуль Node.js.

Вывод

Прочитав эту статью, вы должны иметь представление о PhantomJS. Одна из самых приятных особенностей PhantomJS — простота использования. Если вы уже знакомы с JavaScript, кривая обучения минимальна. PhantomJS также поддерживает множество других функций, которые не были рассмотрены в этой статье. Как всегда, я призываю вас проверить документацию . Есть также ряд примеров, демонстрирующих PhantomJS во всей красе!