JavaScript, Node, Puppeteer: автоматизация Chrome и веб-скрапинг
Предварительная подготовка
Перед началом работы вам понадобится Node 8+. Найти и загрузить его можно здесь, выбрав текущую (Current) версию. Если вы никогда раньше не работали с Node, взгляните на эти учебные курсы или поищите другие материалы, благо, их в Сети предостаточно.
Пример №1: создание копий экрана
После установки puppeteer разберём простой пример. Он, с небольшими изменениями, повторяет документацию к библиотеке. Код, который мы сейчас рассмотрим, делает скриншот заданной веб-страницы.
Для начала создадим файл test.js и поместим в него следующее:
Построчно разберём этот код. Сначала покажем общую картину.
В этой строке мы подключаем ранее установленную библиотеку puppeteer в качестве зависимости.
Функция getPic() завершает работу и мы закрываем браузер.
Запуск примера
Вот что получится после того, как он успешно отработает:
Замечательно! А теперь, чтобы было веселей (и чтобы облегчить отладку), мы можем выполнить те же действия, запустив Chrome в обычном режиме.
Что бы это значило? Попробуйте и увидите сами. Для этого нужно заменить эту строку кода:
Сохраним файл и снова его запустим с помощью Node:
Здорово, правда? Передавая объект
Прежде чем идти дальше, сделаем ещё кое-что. Вы заметили, что скриншот, который делает программа, включает в себя лишь часть страницы? Так происходит из-за того, что окно браузера немного меньше размера веб-страницы. Исправить это можно с помощью следующей строчки, меняющей размер окна:
Её надо добавить в код сразу после команды перехода по URL. Это приведёт к тому, что программа сделает скриншот, который выглядит гораздо лучше:
Вот как будет выглядеть итоговый вариант кода:
Пример №2: веб-скрапинг
Мы будем собирать данные с сайта Books To Scrape. Это — имитация электронного книжного магазина, созданная для экспериментов по веб-скрапингу.
В идеале, после разбора первого примера, вы уже должны понять то, как устроен этот код. Но если это не так — ничего страшного.
Проверим этот код, добавив в функцию scrape() возврат строки:
▍Шаг 1: настройка
Сначала надо создать экземпляр браузера, открыть новую страницу и перейти по URL. Вот как мы всё это сделаем:
Здесь создаём новую страницу в браузере.
Тут добавляем задержку в 1000 миллисекунд для того, чтобы дать браузеру время на полную загрузку страницы, но обычно этот шаг можно опустить.
Здесь закрываем браузер и возвращаем результат.
Предварительная подготовка завершена, теперь займёмся скрапингом.
▍Шаг 2: скрапинг
Как вы уже, наверное, поняли, на сайте Books To Scrape имеется большой каталог настоящих книг, снабжённых условными данными. Мы собираемся взять первую книгу, расположенную на странице, и вернуть её название и цену. Вот домашняя страница сайта. Щёлкнем по первой книге (она выделена красной рамкой).
В документации по puppeteer можно найти метод, который позволяет имитировать щелчки мышью по странице:
Конструкция вида selector представляет собой селектор для поиска элемента, по которому нужно щёлкнуть. Если обнаружено несколько элементов, удовлетворяющих селектору, то щелчок будет сделан по первому из них.
Очень хорошо то, что инструменты разработчика Google Chrome позволяют, без особых сложностей, определить селектор конкретного элемента. Для того, чтобы это сделать, достаточно щёлкнуть правой кнопкой мыши по изображению и выбрать команду Inspect (Просмотреть код).
Эта команда откроет панель Elements (Элементы), в которой будет представлен код страницы, фрагмент которого, соответствующий интересующему нас элементу, будет выделен. После этого можно щёлкнуть по кнопке с тремя точками слева и в появившемся меню выбрать команду Copy → Copy selector (Копировать → Копировать селектор).
Отлично! Теперь у нас имеется селектор и всё готово для того, чтобы сформировать метод click и вставить его в программу. Вот как это будет выглядеть:
Теперь программа имитирует щелчок по первому изображению товара, что приводит к открытию страницы этого товара.
На этой новой странице нас интересует название книги и её цена. Они выделены на нижеприведённом рисунке.
Для начала вызовем метод page.evaluate() и присвоим возвращённое им значение константе result :
В этой функции мы можем выбирать необходимые элементы. Для того, чтобы понять, как описать то, что нам нужно, снова воспользуемся инструментами разработчика Chrome. Для этого щёлкнем правой кнопкой по названию книги и выберем команду Inspect (Просмотреть код).
Такой же подход поможет нам выяснить то, как взять со страницы цену книги.
Теперь, когда мы вытащили со страницы название книги и её цену, мы можем возвратить всё это из функции в виде объекта:
В результате получается следующий код:
Теперь осталось лишь вернуть константу result и вывести её содержимое в консоль.
Полный код этого примера будет выглядеть так:
Теперь можно запустить программу с помощью Node:
Если всё сделано правильно, в консоль будет выведено название книги и её цена:
Собственно говоря, всё это и есть веб-скрапинг и вы только что сделали первые шаги в этом занятии.
Пример №3: улучшаем программу
Тут у вас могут появиться вполне резонные вопросы: «Зачем щёлкать по ссылке, ведущей к странице книги, если и её название, и цена, отображаются на домашней странице? Почему бы не взять их прямо оттуда? И, если мы смогли это сделать, почему бы не прочитать названия и цены всех книг?».
Ответ на эти вопросы заключается в том, что существует множество подходов к веб-скрапингу! К тому же, если ограничиться данными, выводимыми на домашней странице, можно столкнуться с тем, что названия книг будут укорочены. Однако, все эти размышления дают вам отличную возможность попрактиковаться.
▍Задача
Ваша цель — считать все заголовки книг и их цены с домашней страницы и вернуть их в виде массива объектов. Вот какой массив получился у меня:
Можете приступать. Не читайте пока дальше, попробуйте сделать всё сами. Надо сказать, что эта задача очень похожа на ту, которую мы только что решили.
Получилось? Если нет — тогда вот подсказка.
▍Подсказка
Главное отличие этой задачи от предыдущего примера заключается в том, что тут нам надо пройтись по списку данных. Вот как это можно сделать:
Если и сейчас вам не удаётся решить задачу, в этом нет ничего страшного. Это — вопрос практики. Вот один из возможных вариантов её решения.
▍Решение задачи
Итоги
Из этого материала вы узнали о том как пользоваться браузером Google Chrome и библиотекой Puppeteer для создания системы веб-скрапинга. А именно, мы рассмотрели структуру кода, способы программного управления браузером, методику создания копий экрана, методы имитации работы пользователя со страницей и подходы к чтению и сохранению данных, размещаемых на веб-страницах. Если это было ваше первое знакомство с веб-скрапингом, надеемся, теперь у вас есть всё необходимое для того, чтобы вытащить из интернета всё, что вам нужно.
Уважаемые читатели! Пользуетесь ли вы библиотекой Puppeteer и браузером Google Chrome без пользовательского интерфейса?
Что такое API
Содержание
— А зачем это мне? Я вообще-то web тестирую! Вот если пойду в автоматизацию, тогда да… Ну, еще это в enterprise тестируют, я слышал…
А вот и нет! Про API полезно знать любому тестировщику. Потому что по нему системы взаимодействуют между собой. И это взаимодействие вы видите каждый день даже на самых простых и захудалых сайтах.
Любая оплата идет через API платежной системы. Купил билет в кино? Маечку в онлайн-магазине? Книжку? Как только жмешь «оплатить», сайт соединяет тебя с платежной системой.
Но даже если у вас нет интеграции с другими системами, у вас всё равно есть API! Потому что система внутри себя тоже общается по api. И пока фронт-разработчик усиленно пилит GUI (графический интерфейс), вы можете:
Что такое API
API (Application programming interface) — это контракт, который предоставляет программа. «Ко мне можно обращаться так и так, я обязуюсь делать то и это».
Если переводить на русский, это было бы слово «договор». Договор между двумя сторонами, как договор на покупку машины:
API — набор функций
Когда вы покупаете машину, вы составляете договор, в котором прописываете все важные для вас пункты. Точно также и между программами должны составляться договоры. Они указывают, как к той или иной программе можно обращаться.
Соответственно, API отвечает на вопрос “Как ко мне, к моей системе можно обратиться?”, и включает в себя:
Тут вы можете мне сказать:
— Хмм, погоди. Операция, данные на входе, данные на выходе — как-то всё это очень сильно похоже на описание функции!
Если вы когда-то сталкивались с разработкой или просто изучали язык программирования, вы наверняка знаете, что такое функция. Фактически у нас есть данные на входе, есть данные на выходе, и некая магия, которая преобразует одно в другое.
И да! Вы будете правы в том, что определения похожи. Почему? Да потому что API — это набор функций. Это может быть одна функция, а может быть много.
Как составляется набор функций
Да без разницы как. Как разработчик захочет, так и сгруппирует. Например, можно группировать API по функционалу. То есть:
Можно не группировать вообще, а делать одно общее API.
Можно сделать одно общее API, а остальные «под заказ». Если у вас коробочный продукт, то в него обычно входит набор стандартных функций. А любые хотелки заказчиков выносятся отдельно.
Получается, что в нашей системе есть несколько разных API, на каждое из которых у нас написан контракт. В каждом контракте четко прописано, какие операции можно выполнять, какие функции там будут
И конечно, функции можно переиспользовать. То есть одну и ту же функцию можно включать в разные наборы, в разные апи. Никто этого не запрещает.
Получается, что разработчик придумывает, какое у него будет API. Либо делает общее, либо распределяет по функционалу или каким-то своим критериям, и в каждое апи добавляет тот набор функций, который ему необходим.
При чем тут слово «интерфейс»
— Минуточку, Оля! Ты же сама выше писала, что API — это Application programming interface. Почему ты тогда говоришь о контракте, хотя там слово интерфейс?
Да потому, что в программировании контракт — это и есть интерфейс. В классическом описании ООП (объектно-ориентированного программирования) есть 3 кита:
Не всегда программа предоставляет именно графический интерфейс. Это может быть SOAP, REST интерфейс, или другое API. Чтобы использовать этот интерфейс, вы должны понимать:
Как вызывается API
Вызвать апи можно как напрямую, так и косвенно.
Вызов API напрямую
1. Система вызывает функции внутри себя
Разные части программы как-то общаются между собой. Они делают это на программном уровне, то есть на уровне API!
Это самый «простой» в использовании способ, потому что автор API, которое вызывается — разработчик. И он же его потребитель! А значит, проблемы с неактуальной документацией нет =)
Шучу, проблемы с документацией есть всегда. Просто в этом случае в качестве документации будут комментарии в коде. А они, увы, тоже бывают неактуальны. Или разработчики разные, или один, но уже забыл, как делал исходное api и как оно должно работать…
2. Система вызывает метод другой системы
А вот это типичный кейс, которые тестируют тестировщики в интеграторах. Или тестировщики, которые проверяют интеграцию своей системы с чужой.
Одна система дергает через api какой-то метод другой системы. Она может попытаться получить данные из другой системы. Или наоборот, отправить данные в эту систему.
Допустим, я решила подключить подсказки из Дадаты к своему интернет-магазинчику, чтобы пользователь легко ввел адрес доставки.
Я подключаю подсказки по API. И теперь, когда пользователь начинает вводить адрес на моем сайте, он видит подсказки из Дадаты. Как это получается:
И, конечно, не забываем про кейс, когда мы разрабатываем именно API-метод. Который только через SOAP и можно вызвать, в интерфейсе его нигде нет. Что Заказчик заказал, то мы и сделали ¯\_(ツ)_/¯
Пример можно посмотреть в Users. Метод MagicSearch создан на основе реальных событий. Хотя надо признать, в оригинале логика еще замудренее была, я то под свой сайт подстраивала.
Но тут фишка в том, что в самой системе в пользовательском интерфейсе есть только обычный поиск, просто строка ввода. Ну, может, парочка фильтров. А вот для интеграции нужна была целая куча доп возможностей, что и было сделано через SOAP-метод.
Функционал супер-поиска доступен только по API, пользователь в интерфейсе его никак не пощупает.
В этом случае у вас обычно есть ТЗ, согласно которому работает API-метод. Ваша задача — проверить его. Типичная задача тестировщика, просто добавьте к стандартным тестам на тест-дизайн особенности тестирования API, и дело в шляпе!
(что именно надо тестировать в API — я расскажу отдельной статьей чуть позднее)
3. Человек вызывает метод
Для примера снова идем в Users. Если мы хотим создать пользователя, надо заполнить уйму полей!
Конечно, это можно сделать в помощью специальных плагинов типа Form Filler. Но что, если вам нужны адекватные тестовые данные под вашу систему? И на русском языке?
Заполнение полей вручную — грустно и уныло! А уж если это надо повторять каждую неделю или день на чистой тестовой базе — вообще кошмар. Это сразу первый приоритет на автоматизацию рутинных действий.
И в данном случае роль автоматизатора выполняет… Postman. Пользователя можно создать через REST-запрос CreateUser. Один раз прописали нормальные “как настоящие” данные, каждый раз пользуемся. Профит!
Вместо ручного заполнения формы (1 минута бездумного заполнения полей значениями «лпрулпк») получаем 1 секунду нажатия на кнопку «Send». При этом значения будут намного адекватнее.
А еще в постмане можно сделать отдельную папку подготовки тестовой базы, напихать туда десяток запросов. И вот уже на любой базе за пару секунд вы получаете столько данных, сколько вручную вбивали бы часами!
Если вы нашли баг и не понимаете, на кого его вешать — разработчика front-end или back-end, уберите все лишнее. Вызовите метод без графического интерфейса. А еще вы можете тестировать логику программы, пока интерфейс не готов или сломан.
4. Автотесты дергают методы
Есть типичная пирамида автоматизации:
Слово API как бы намекает на то, что будет использовано в тестах ツ
GUI-тесты — честный тест, робот делает все, что делал бы пользователь. Открывает браузер, тыкает на кнопочки… Но если что-то упадет, будете долго разбираться, где именно.
API-тесты — все то же самое, только без браузера. Мы просто подаем данные на вход и проверяем данные на выходе. Например, можно внести итоговый ответ в эксельку, и пусть робот выверяет ее, правильно ли заполняются данные? Локализовать проблему становится проще.
Unit-тесты — это когда мы проверяем каждую функцию отдельно. Отдельно смотрим расчет для ячейки 1, отдельно — для ячейки 2, и так далее. Такие тесты шустрее всего гоняются и баги по ним легко локализовать.
Косвенный вызов API
Когда пользователь работает с GUI, на самом деле он тоже работает с API. Просто не знает об этом, ему это просто не нужно.
То есть когда пользователь открывает систему и пытается загрузить отчет, ему не важно, как работает система, какой там magic внутри. У него есть кнопочка «загрузить отчет», на которую он и нажимает. Пользователь работает через GUI (графический пользовательский интерфейс).
Но на самом деле под этим графическим пользовательским интерфейсом находится API. И когда пользователь нажимает на кнопочку, кнопочка вызывает функцию построения отчета.
А функция построения отчета уже может вызывать 10 разных других функций, если ей это необходимо.
И вот уже пользователь видит перед собой готовый отчет. Он вызвал сложное API, даже не подозревая об этом!
Что значит «Тестирование API»
В первую очередь, мы подразумеваем тестирование ЧЕРЕЗ API. «Тестирование API» — общеупотребимый термин, так действительно говорят, но технически термин некорректен. Мы не тестируем API, мы не тестируем GUI (графический интерфейс). Мы тестируем какую-то функциональность через графический или программный интерфейс.
Но это устоявшееся выражение. Можно использовать его и говорить “тестирование API”. И когда мы про это говорим, мы имеем в виду:
Когда мы говорим про тестирование API, чаще всего мы подразумеваем тестирование Remote API. Когда у нас есть две системы, находящихся на разных компьютерах, которые как-то между собой общаются.
И если вы видите в вакансии «тестирование API», скорее всего это подразумевает умение вызвать SOAP или REST сервис и протестировать его. Хотя всегда стоит уточнить!
Резюме
API (Application programming interface) — это контракт, который предоставляет программа. «Ко мне можно обращаться так и так, я обязуюсь делать то и это».
Контракт включает в себя:
Введение
Например, ссылки на другие статьи в энциклопедиях — это тоже гиперссылки.
Гипертекстовые документы в Интернете называются веб-страницами ; обычно ссылки на веб-страницах выделяются цветом и подчёркиваются.
Современные веб-документы включают не только текст, но и другие виды информации — рисунки, звук, видео, поэтому для них используется выражение гипермедиа.
Гипермедиа (англ. hypermedia) — это гипертекстовый документ, содержащий изображения, звук, видео, причём каждый элемент может быть гиперссылкой.
Веб-страницы размещаются на серверах. Слово «сервер» имеет несколько значений. Сервером называют специальный компьютер, который выделен для обслуживания пользователей (например, для хранения файлов). Другое значение термина «сервер» — программа, которая обеспечивает работу какой-нибудь службы.
Например, для того чтобы пользователи могли обращаться к веб-страницам, на компьютере, где они хранятся, нужно запустить веб-сервер.
Для просмотра веб-страниц на экране нужна специальная программа — браузер (англ. browser — «просмотрщик»). Браузер — это клиентская программа, её задача — послать запрос веб-серверу, получить в ответ веб-страницу и показать её на экране.
Статические и динамические веб-страницы
Веб-страницы — это обычные текстовые файлы (в формате
«только текст», англ. plain text). Для того чтобы определить структуру документа (заголовки, абзацы, списки и др.), используют язык HTML (англ. HyperText Markup Language — язык разметки гипертекста).
В языке HTML используются команды особого типа — тэги (англ. tag — метка, ярлык). Существуют тэги для выделения заголовков, абзацев, вставки таблиц. С помощью тэгов в веб-страницы добавляют рисунки, звуки, анимацию, видео, которые хранятся на сервере в виде отдельных файлов. Часто для дополнительных данных на сайте создаются специальные каталоги, например, рисунки могут быть размещены в каталоге images, звуковые и видеофайлы — в каталоге media (рис. 4.1).
