Не используя библиотеки для парсинга распарсить получить определенные данные файл логов web сервера

Обновлено: 04.07.2024

Этичный хакинг и тестирование на проникновение, информационная безопасность

Что такое парсинг

Парсинг (parsing) – это буквально с английского «разбор», «анализ». Под парсингом обычно имеют ввиду нахождение, вычленение определённой информации.

Парсинг сайтов может использоваться при создании сервиса, а также при тестировании на проникновение. Для пентестера веб-сайтов, навык парсинга сайтов является базовыми, т.е. аудитор безопасности (хакер) должен это уметь.

Умение правильно выделить информацию зависит от мастерства владения регулярными выражениями и командой grep (очень рекомендую это освоить – потрясающее испытание для мозга!). В этой же статье будут рассмотрены проблемные моменты получения данных, поскольку все сайты разные и вас могут ждать необычные ситуации, которые при первом взгляде могут поставить в тупик.


Подобным образом можно автоматизировать получение и извлечение любой информации с любого сайта.

Особенности парсинга веб-сайтов

Одной из особенностей парсинга веб-сайтов является то, что как правило мы работаем с исходным кодом страницы, т.е. HTML кодом, а не тем текстом, который показывается пользователю. Т.е. при создании регулярного выражения grep нужно основываться на исходном коде, а не на результатах рендеринга. Хотя имеются инструменты и для работы с текстом, получающимся в результате рендеринга веб-страницы – об этом также будет рассказано ниже.

В этом разделе основной упор сделан на парсинг из командной строки Linux, поскольку это самая обычная (и привычная) среда работы для тестера на проникновение веб-приложений. Будут показаны примеры использования разных инструментов, доступных из консоли Linux. Тем не менее, описанные здесь приёмы можно использовать в других операционных системах (например, cURL доступна и в Windows), а также в качестве библиотеки для использования в разных языках программирования.

Подразумевается, что вы понимаете принципы работы командной строки Linux. Если это не так, то рекомендуется ознакомиться с циклом:

Получение содержимого сайта в командной строке

Самым простым способом получения содержимого веб-страницы, а точнее говоря, её HTML кода, является команда вида:

В качестве ХОСТа может быть адрес сайта (URL) или IP. На самом деле, curl поддерживает много разных протоколов – но здесь мы говорим именно о сайтах.

Чтобы в командной строке присвоить полученные данные переменной, можно использовать следующую конструкцию:

  • HTMLCode – имя переменной (обратите внимание, что при присвоении (даже повторном) имя переменной пишется без знака доллара ($), а при использовании переменной, знак доллара всегда пишется.
  • ="$(КОМАНДА)" – конструкция выполнения КОМАНДЫ без вывода результата в консоль; результат выполнения команды присваивается переменной. Обратите внимание, что ни до, ни после знака равно (=) нет пробелов – это важно, иначе возникнет ошибка.

Также полученное содержимое веб-страницы зачастую передаётся по трубе для обработке в других командах:

Реальные примеры даны чуть ниже.

Отключение статистики при использовании cURL

Когда вы будете передавать полученное содержимое по трубе (|), то вы увидите, что команда curl показывает статистику о скорости, времени, количестве переданных данных:


Чтобы отключить вывод статистики, используйте опцию -s, например:

Автоматически следовать редиректам с cURL

Вы можете дать указание cURL следовать редиректам, т.е. открывать страницу, на которую делает редирект (перенаправление) та страница, которую мы в данный момент пытаемся открыть.


Чтобы curl переходила по перенаправлением используется опция -L:

Подмена User Agent при использовании cURL

Удалённый сервер видит, какая программа пытается к нему подключиться: это веб-браузер, или поисковый робот, или кто-то ещё. По умолчанию cURL передаёт в качестве User-Agent что-то вроде «curl/7.58.0». Т.е. сервер видит, что подключается не веб-браузер, а консольная утилита.

Некоторые веб-сайты не хотят ничего показывать консольным утилитам, например, если при обычном запросе вида:

Получение в cURL страниц со сжатием

Иногда при использовании cURL появляется предупреждение:

Причина в том, что веб-страница передаётся с использованием компрессии (сжатия), чтобы увидеть данные достаточно использовать опцию --compressed:

В результате будет выведен обычный HTML код запрашиваемой страницы.

Неправильная кодировка при использовании cURL

В настоящее время на большинстве сайтов используется кодировка UTF-8, с которой cURL прекрасно работает.

Но, например, при открытии некоторых сайтов:

Вместо кириллицы мы увидим крякозяблы:


Кодировку можно преобразовать «на лету» с помощью команды iconv. Но нужно знать, какая кодировка используется на сайте. Для этого обычно достаточно заглянуть в исходный код веб-страницы и найти там строку, содержащую слово charset, например:

Эта строка означает, что используется кодировка windows-1251.

Для преобразования из кодировки windows-1251 в кодировку UTF-8 с помощью iconv команда выглядит так:

Совместим её с командой curl:

После этого вместо крякозяблов вы увидите русские буквы.

Изменение реферера с cURL

Реферер (Referrer URL) – это информация о странице, с которой пользователь пришёл на данную страницу, т.е. это та страница, на которой имеется ссылка, по которой кликнул пользователь, чтобы попасть на текущую страницу. Иногда веб-сайты не показывают информацию, если пользователь не содержит в качестве реферера правильную информацию (либо показывают различную информацию, в зависимости от типа реферера (поисковая система, другая страница этого же сайта, другой сайт)). Вы можете манипулировать значением реферера используя опцию -e. После которой в кавычках укажите желаемое значение. Реальный пример будет чуть ниже.

Иногда веб-сайты требуют имя пользователя и пароль для просмотра их содержимого. С помощью опции -u вы можете передать эти учётные данные из cURL на веб-сервер как показано ниже.

По умолчанию curl использует базовую аутентификацию. Мы можем задать иные методы аутентификации используя --ntlm | --digest.

cURL и аутентификация в веб-формах (передача данных методом GET и POST)

Аутентификация в веб-формах – это тот случай, когда мы вводим логин и пароль в форму на сайте. Именно такая аутентификация используется при входе в почту, на форумы и т. д.

1) С помощью Burp Suite или Wireshark узнать, как именно происходит передача данных. Необходимо знать: адрес страницы, на которую происходит передача данных, метод передачи (GET или POST), передаваемая строка.

2) Когда информация собрана, то curl запускается дважды – в первый раз для аутентификации и получения кукиз, второй раз – с использованием полученных кукиз происходит обращение к странице, на которой содержаться нужные сведения.

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

Для сохранения кукиз используется опция --cookie-jar, после которой нужно указать имя файла. Для передачи данных методом POST используется опция --data. Пример (пароль заменён на неверный):

Далее для получения информации со страницы, доступ на которую имеют только зарегестрированные пользователи, нужно использовать опцию -b, после которой нужно указать путь до файла с ранее сохранёнными кукиз:

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

Извлечение информации из заголовков при использовании cURL

Иногда необходимо извлечь информацию из заголовка, либо просто узнать, куда делается перенаправление.

В этой команде имеются уже знакомые нам опции -s (подавление вывода) и -A (для указания своего пользовательского агента).

Новой опцией является -I, которая означает показывать только заголовки. Т.е. не будет показываться HTML код, поскольку он нам не нужен.

На этом скриншоте видно, в какой именно момент отправляется информация о новой ссылке для перехода:


Аналоогичный пример для хорошо известной в определённых кругах программы Maltego (когда-то на сайте отсутствовала информация о версии и пришлось писать парсер заголовков):

Обратите внимание, что в этой команде не использовалась опция -I, поскольку она вызывает ошибку:

В последней команде используется новая для нас опция -v, которая увеличивает вербальность, т.е. количество показываемой информации. Но особенностью опции -v является то, что она дополнительные сведения (заголовки и прочее) выводит не в стандартный вывод (stdout), а в стандартный вывод ошибок (stderr). Хотя в консоли всё это выглядит одинаково, но команда grep перестаёт анализировать заголовки (как это происходит в случае с -I, которая выводит заголовки в стандартный вывод). В этом можно убедиться используя предыдущую команду без 2>&1:

Строка с Location никогда не будет найдена, хотя на экране она явно присутствует.

Конструкция 2>&1 перенаправляет стандартный вывод ошибок в стандартный вывод, в результате внешне ничего не меняется, но теперь grep может обрабатывать эти строки.

Более сложная команда для предыдущего обработчика форм (попробуйте в ней разобраться самостоятельно):

Парсинг сайта, на котором текст создаётся с помощью JavaScript

Если контент веб-страницы формируется методами JavaScript, то можно найти необходимый файл с кодом JavaScript и парсить его. Но иногда код слишком сложный или даже обфусцированный. В этом случае поможет PhantomJS.

Особенностью PhantomJS ялвяется то, что это настоящий инструмент командной строки, т.е. может работать на безголовых машинах. Но при этом он может получать содержимое веб-страницы так, как будто бы вы её открыли в обычном веб-браузере, в том числе после работы JavaScript.

Можно получать веб-страницы как изображение, так и просто текст, выводимый пользователю.

Для его запуска использую PhantomJS:

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

Чтобы отфильтровать нужные мне сведения о последней сборке:

Будет выведено что-то вроде 15063.877.

Парсинг в командной строке RSS, XML, JSON и других сложных форматов

RSS, XML, JSON и т.п. – это текстовые файлы, в которых данные структурированы определённым образом. Для разбора этих файлов можно, конечно, использовать средства Bash, но можно сильно упростить себе задачу, если задействовать PHP.

В PHP есть ряд готовых классов (функций), которые могут упростить задачу. Например SimpleXML для обработки RSS, XML. JSON для JavaScript Object Notation.

Запустить файл можно так:


Кстати, cURL можно было бы использовать прямо из PHP скрипта. Поэтому можно парсить в PHP не прибегая к услугам Bash. В качестве примера, создайте файл parseXML2.php со следующем содержимым:

И запустите его следующим образом из командной строки:

Этот же самый файл parseXML2.php можно поместить в директорию веб-сервера и открыть в браузере.

Заключение

Здесь рассмотрены ситуации, с которыми вы можете столкнуться при парсинге веб-сайтов. Изученный материал поможет лучше понимать, что происходит в тот момент, когда вы подключаетесь к веб-сайту. Поскольку пентестер часто использует не веб-браузеры для работы с веб-сайтами: различные сканеры и инструменты, то знание о реферерах, User Agent, кукиз, заголовках, кодировках, аутентификации поможет быстрее разобраться с проблемой, если она возникнет.

Для часто встречающихся задач (сбор email адресов, ссылок) уже существует достаточно много инструментов – и часто можно взять готовое решение, а не изобретать велосипед. Но с помощью curl и других утилит командной строки вы сможете максимально гибко настроить получение данных с любого сайта.

Постоянно в Интернете, ничего не успеваете? Парсинг сайта спешит на помощь! Разбираемся, как автоматизировать получение нужной информации.

Осваиваем парсинг сайта: короткий туториал на Python

Чтобы быть в курсе, кто получит кубок мира в 2019 году, или как будет выглядеть будущее страны в ближайшие 5 лет, приходится постоянно зависать в Интернете. Но если вы не хотите тратить много времени на Интернет и жаждете оставаться в курсе всех событий, то эта статья для вас. Итак, не теряя времени, начнём!

Доступ к новейшей информации получаем двумя способами. Первый – с помощью API, который предоставляют медиа-сайты, а второй – с помощью парсинга сайтов (Web Scraping).

Использование API предельно просто, и, вероятно, лучший способ получения обновлённой информации – вызвать соответствующий программный интерфейс. Но, к сожалению, не все сайты предоставляют общедоступные API. Таким образом, остаётся другой путь – парсинг сайтов.

Парсинг сайта

Последовательность действий

  • Получить URL страницы, с которой хотим извлечь данные.
  • Скопировать или загрузить HTML-содержимое страницы.
  • Распарсить HTML-содержимое и получить необходимые данные.

Эта последовательность помогает пройти по URL-адресу нужной страницы, получить HTML-содержимое и проанализировать необходимые данные. Но иногда требуется сперва войти на сайт, а затем перейти по конкретному адресу, чтобы получить данные. В этом случае добавляется ещё один шаг для входа на сайт.

Пакеты

Для анализа HTML-содержимого и получения необходимых данных используется библиотека Beautiful Soup. Это удивительный пакет Python для парсинга документов формата HTML и XML.

Для входа на веб-сайт, перехода к нужному URL-адресу в рамках одного сеанса и загрузки HTML-содержимого будем использовать библиотеку Selenium. Selenium Python помогает при нажатии на кнопки, вводе контента и других манипуляциях.

Погружение в код

Сначала импортируем библиотеки, которые будем использовать:

Затем укажем драйверу браузера путь к Selenium, чтобы запустить наш веб-браузер (Google Chrome). И если не хотим, чтобы наш бот отображал графический интерфейс браузера, добавим опцию headless в Selenium.

Браузеры без графического интерфейса (headless) предоставляют автоматизированное управление веб-страницей в среде, аналогичной популярным веб-браузерам, но выполняются через интерфейс командной строки или с использованием сетевых коммуникаций.

После настройки среды путём определения браузера и установки библиотек приступаем к HTML. Перейдём на страницу входа и найдём идентификатор, класс или имя полей для ввода адреса электронной почты, пароля и кнопки отправки, чтобы ввести данные в структуру страницы.

После успешного входа в систему перейдём на нужную страницу и получим HTML-содержимое страницы.

Когда получили HTML-содержимое, единственное, что остаётся, – парсинг. Распарсим содержимое с помощью библиотек Beautiful Soup и html5lib.

html5lib – это пакет Python, который реализует алгоритм парсинга HTML5, на который сильно влияют современные браузеры. Как только получили нормализованную структуру содержимого, становится доступным поиск данных в любом дочернем элементе тега html . Искомые данные присутствуют в теге table , поэтому ищем этот тег.

Один раз находим родительский тег, а затем рекурсивно проходим по дочерним элементам и печатаем значения.

Так парсятся данные с любого сайта.

Если же парсим веб-сайт, который часто обновляет контент, например, результаты спортивных соревнований или текущие результаты выборов, целесообразно создать задание cron для запуска этой программы через конкретные интервалы времени.

Команда INOSTUDIO

Время от времени разработчик сталкивается с задачами, связанными со сбором какой-то информации с различных сайтов для анализа или поддержания какого-либо процесса. Хорошо, если сайт имеет API, с помощью которого можно получить нужную информацию, но иногда API отсутствует, либо не отдаёт нужную информацию. Что делать в таком случае?

Введение

Можно извлекать информацию вручную, что работает на небольших объёмах данных, либо автоматизировать данный процесс, что позволяет извлекать большие объёмы информации с сайтов. Впрочем автоматический сбор информации без трудностей не обходится.

Процесс извлечения структурированной полезной информации с сайта называется парсингом (parsing), а инструменты для реализации данного процесса — парсерами (parsers).

Полученные данные можно использовать самыми различными способами, к примеру:

  • маркетинговые исследования;
  • мониторинг СМИ в реальном времени;
  • анализ общественного мнения;
  • автоматическое ценообразование (при разумном применении) на базе анализа цен конкурентов;
  • построение списка потенциальных пользователей на базе информации о пользователях ресурсов конкурентов;
  • создание API для сайтов без API.

Как правило, сайты разрабатываются с учётом того, что считывать информацию с их страниц будет человек. Но формат представленных данных, понятный человеку, зачастую не столь понятен программным средствам. Кроме того, структура представляемых данных варьируется от сайта к сайту, поэтому не существует универсального средства для извлечения информации с них.

Что такое парсеры?

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

Также существуют и готовые решения, позволяющие извлекать информацию с сайта после предварительной конфигурации, не написав ни единой строчки кода. Эти решения часто стоят дорого и не обладают той гибкостью, которую могут дать решения, разработанные под конкретный сайт.

Методы извлечения информации

Можно выделить следующие методы извлечения информации с сайтов:

  1. Ручной — в данном случае роль парсера выполняет человек. Он производит всю цепочку действий, необходимую для получения требуемой информации. Информация собирается обычным копипастом.
  2. Гибридный — пользователь по-прежнему выполняет основные действия для получения информации, но может использовать вспомогательные программные средства для автоматизации сбора, например, браузерный плагин, который на основе конфигурации извлекает и структурирует информацию из определенных мест страницы при активации.
  3. Автоматический — получение и структурирование информации выполняется автоматически.

Для того чтобы один раз получить значения нескольких полей с 10−15 страниц, писать отдельный парсер может быть и нецелесообразно, однако в случае большого числа страниц, полей или высокой периодичности сбора информации, стоит задуматься об автоматизации данного процесса.

Процесс извлечения информации с отдельной веб-страницы можно разбить на следующие этапы:

  1. Построение запроса для получения информации.
  2. Выполнение запроса и получение ответа.
  3. Обработка ответа, извлечение и структурирование необходимой информации.
  4. Передача полученной информации для последующей обработки.

Процесс извлечения информации может быть простым: загрузить URL, считать информацию и отдать получателю; а может быть и сложным: авторизоваться в системе, сконструировать запрос по информации из заголовков и значений JavaScript-переменных на странице, имя которых может меняться с каждым запросом, а JS-код находиться в минифицированном или обфусцированном виде.

Если в первом случае всё достаточно просто, то во втором, чтобы за разумное время разработать парсер, стоит прибегнуть к использованию headless-браузеров (без графического интерфейса) с поддержкой сценариев вроде PhantomJS для извлечения данных для оптимизации времени на изучение того, как сайт взаимодействует с бэкендом. Также для этих целей можно прибегнуть к Selenium WebDriver с одним из реальных браузеров.

Инструменты

Для автоматизированного извлечения информации с веб-страниц существует 3 типа инструментов:

1. Библиотеки.

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

К таким инструментам относятся многочисленные библиотеки для различных языков программирования: jSoup для Java, SimpleHTMLDom для PHP, lxml.html для Python и другие.

2. Headless-браузеры.

Данный подход позволяет обрабатывать страницу в браузере с поддержкой JavaScript, что позволяет писать свои сценарии для получения требуемой информации и даже использовать JavaScript библиотеки вроде jQuery для извлечения информации со страницы, что ускоряет разработку парсеров. Отсутствие графического интерфейса позволяет запускать данные браузеры даже на серверах, поддерживающих только консольный режим.

К таким инструментам можно отнести PhantomJS и SlimerJS.

3. SaaS решения.

Данные сервисы предоставляют графический интерфейс, с помощью которого можно указать адрес страницы, указать блоки, из которых нужно извлечь информацию, а также создать ряд правил по извлечению данных. Такие сервисы не обладают той гибкостью, которую предоставляют низкоуровневые решения. Некоторые из них стоят довольно дорого, зато ими просто пользоваться.

К таким сервисам можно отнести Mozenda, Octoparse (бесплатный), Import.io (бесплатный).

4. Настольные приложения.

Предоставляют графический интерфейс с возможностью задания правил для извлечения информации со страницы. Как и SaaS решения, они облегчают работу пользователя, избавляя его от необходимости написания кода, обладают определенной гибкостью, но не могут сравниться с решениями, разрабатываемыми под конкретный сайт.

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

Способы защиты от парсеров

Для владельцев сайтов с полезной информацией парсеры — нежеланные гости. Они впустую расходуют вычислительные ресурсы сервера, трафик, за который возможно нужно платить, а плохо спроектированные парсеры могут обрушить огромное число запросов на сервер приводя к DoS (Denial of Service). Извлечённые данные могут использоваться в корыстных целях, вроде воровства материалов с одного ресурса и публикации на другом от чужого имени, нарушая авторство.

В связи с этим веб-мастера могут применять различные меры борьбы с автоматическими парсерами, а именно:

  1. Динамическое изменение структуры кода страницы с целью усложнения извлечения информации из блоков.
  2. Показ капчи при авторизации/регистрации/превышении числа запросов за единицу времени.
  3. Блокировка клиентов по среднему объему трафика в единицу времени.
  4. Анализ поведения клиентов и блокировка/требование пройти капчу для продолжения работы при подозрительном поведении.

Эти проблемы решаемы:

  • Если информация очень важна, то можно прибегнуть к screen scraping’у — извлечение информации не из текста страницы, а с её изображения.
  • Распознавание капчи можно передать сторонним сервисам.
  • Растянуть процесс парсинга во времени.
  • Имитировать поведение реального пользователя: клики, движения мыши, пролистывание страницы.

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

Пример из практики

В качестве примера рассмотрим следующую проблему:

Имеется сайт, подключенный к 20+ рекламным биржам, использующим разные системы отчетности. Клиент захотел централизованно собирать информацию с них и вести статистику по доходам/расходам рекламных кампаний посуточно.

Чтобы построить такой отчет, нужно было бы пройтись по всем площадкам, и для каждой выполнить следующий набор действий:

  1. авторизоваться;
  2. сформировать запрос для получения статистики за последние сутки;
  3. получить ответ;
  4. сохранить информацию из ответа в таблицу.

А для получения информации о расходах на рекламу необходимо было ещё дополнительно взять информацию о расходах по каждой рекламной кампании.

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

Идея была следующей:

  1. Под каждую площадку написать свой парсер, который авторизовался бы в системе и получал всю информацию за нас.
  2. Написать CRON задачу, которая выполнялась бы раз в сутки, запускала парсеры, собирала их ответы и сохраняла в базу.
  3. Создать интерфейс, позволяющий визуализировать полученную информацию.

Так как парсеры должны были запускаться на сервере без графического интерфейса, выбор пал на PhantomJS.

Каждый парсер должен был принимать на вход следующие параметры:

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

Для парсеров доходов на выход выдавалась следующая информация:

  • число показов;
  • число кликов;
  • доход;
  • CTR.

А для парсеров расходов:

  • ID кампании;
  • число кликов;
  • расход;
  • название кампании.

Пример исходного кода отдельного парсера:

Таким образом, каждый парсер авторизовывался, считывал информацию и выводил её JSON в STDOUT, где запускающий скрипт её считывал и обрабатывал.

Приведённый пример достаточно простой. Некоторые системы использовали свои конструкторы запросов, имеющие непонятную логику работы. В таких случаях приходилось эмулировать клики по элементам интерфейса и ждать загрузку результатов. В других случаях за счет того, что загрузка шла через AJAX, нужно было ждать появления того или иного элемента на странице, прежде чем можно было считать нужные данные.

К счастью, с капчей в рамках решения данной задачи сталкиваться не пришлось.

На разработку одного парсера в зависимости от сложности системы уходило до 4-х часов (часть площадок использовали одинаковые системы отчетности, что позволяло использовать некоторые парсеры повторно).

Заключение

Разработка парсеров — творческий и увлекательный процесс, в ходе которого можно ознакомиться с архитектурой и особенностями устройства сторонних сайтов. Кроме того, разработка парсеров является довольно востребованной услугой и является ценным навыком при работе с сервисом, который не предоставляет API, что делает данный вопрос актуальным и интересным.


Часто возникает задача периодически парсить какой-нибудь сайт на наличие новой информации. Например, если ты пишешь агрегатор контента с новостного сайта или форума, в котором нет поддержки RSS. Проще всего написать скрепер на Питоне и разобрать полученный HTML через beautifulsoup или регулярками. Однако есть более элегантный способ — самому сделать недостающие API для сайта и получать ответы в привычном JSON, как будто бы у сайта есть нативный API.

Не будем далеко ходить за примером и напишем парсер контента с «Хакера». Как ты знаешь, сайт нашего журнала сейчас не предоставляет никакого API для программного получения статей, кроме RSS. Однако RSS не всегда удобен, да и выдает далеко не всю нужную информацию. Исправим это!

Постановка задачи

Итак, наша задача: сделать API вида GET /posts , который бы отдавал десять последних статей с «Хакера» в JSON. Также нам нужно иметь возможность задавать сдвиг, то есть раз за разом получать следующие десять постов.

Ответ должен быть таким:

Также нужно иметь возможность получать следующие десять постов — со второй страницы, третьей и так далее. Это делается через GET-параметр вида GET /posts?page=2 . Если page в запросе не указан, считаем его равным 1 и отдаем посты с первой страницы «Хакера». В общем, задача ясна, переходим к решению.

Фреймворк для веба

WrapAPI — это довольно новый (пара месяцев от роду) сервис для построения мощных кастомных парсеров веба и предоставления к ним доступа по API. Не пугайся, если ничего не понял, сейчас поясню на пальцах. Работает так:

Немного о приватности запросов

Ты наверняка уже задумался о том, насколько безопасно использовать чужой сервис и передавать ему параметры своих запросов с приватными данными. Тем более что по умолчанию для каждого нового API-проекта будет создаваться публичный репозиторий и запускать API из него сможет любой желающий. Не все так плохо:

  1. Каждый API-репозиторий (а соответственно, и все API-запросы в нем) можно сделать приватным. Они не будут показываться в общем списке уже созданных API на платформе WrapAPI. Просто выбери достаточно сложное имя репозитория, и шанс, что на него кто-то забредет случайно, сведется к минимуму.
  2. Любой запрос к WrapAPI требует специального токена, который нужно получить в своей учетке WrapAPI. То есть просто так узнать URL к твоему репозиторию и таскать через него данные не получится. Токены подразделяются на два типа: серверные и клиентские, для использования прямо на веб-страничке через JavaScript. Для последних нужно указать домен, с которого будут поступать запросы.
  3. Ну и наконец, в скором времени разработчик обещает выпустить self-hosted версию WrapAPI, которую ты сможешь поставить на свой сервер и забыть о проблеме утечек данных (конечно, при условии, что в коде WrapAPI не будет бэкдоров).

Приготовления

Несколько простых шагов перед началом.

  1. Идем на сайт WrapAPI, создаем новую учетку и логинимся в нее.
  2. Устанавливаем расширение для Chrome (подойдет любой Chromium-based браузер), открываем консоль разработчика и видим новую вкладку WrapAPI .
  3. Переходим на нее и логинимся.

Это расширение нам понадобится для того, чтобы перехватывать запросы, которые мы собираемся эмулировать, и быстро направлять их в WrapAPI для дальнейшей работы. По логике работы это расширение очень похоже на связку Burp Proxy + Burp Intruder.

Для работы с WrapAPI нужно повторно авторизоваться еще и в расширении в консоли разработчика Chrome

Для работы с WrapAPI нужно повторно авторизоваться еще и в расширении в консоли разработчика Chrome

Отлавливаем запросы

Для получения постов я предлагаю использовать запрос пагинации, он доступен без авторизации и может отдавать по десять постов для любой страницы «Хакера», возвращая HTML в объекте JSON (см. ниже).

Запросы, которые генерятся по нажатию на ссылки пагинатора, будем использовать как образец

Запросы, которые генерятся по нажатию на ссылки пагинатора, будем использовать как образец

Запрос пойман, сохраняем его на сервер WrapAPI

Запрос пойман, сохраняем его на сервер WrapAPI

Конфигурируем WrapAPI

После того как ты выбрал нужное имя для твоего репозитория (я взял test001 и endpoint posts ) и сохранил его на сервер WrapAPI через расширение для Chrome, иди на сайт WrapAPI и открывай репозиторий. Самое время настраивать наш API.

Обзор нашего будущего API

Обзор нашего будущего API

Переходи на вкладку Inputs and request. Здесь нам понадобится указать, с какими параметрами WrapAPI должен парсить запрашиваемую страницу, чтобы сервер отдал ему валидный ответ.

Конфигурируем входные параметры запроса

Конфигурируем входные параметры запроса

Аккуратно перебей все параметры из пойманной WrapAPI полезной нагрузки (POST body payload) в поле слева. Для всех параметров, кроме paginated , выставь тип Constant . Это означает, что в запросы к серверу будут поставляться предопределенные значения, управлять которыми мы не сможем (нам это и не нужно). А вот для paginated выставляй Variable API , указав имя page . Это позволит нам потом обращаться к нашему API по URL вида GET /posts?page=5 (с query-параметром page ), а на сервер уже будет уходить полноценный POST со всеми перечисленными параметрами.

Заголовки запроса ниже можно не трогать, я использовал стандартные из Chromium. Если парсишь не «Хакер», а данные с какого-нибудь закрытого сервера, можешь подставить туда нужные куки, хедеры, basic-auth и все, что нужно. Одним словом, ты сможешь настроить свой запрос так, чтобы сервер безо всяких подозрений отдал тебе контент.

Выставляем необходимые POST-параметры в формате form/urlencoded, чтобы наш запрос отработал правильно

Выставляем необходимые POST-параметры в формате form/urlencoded, чтобы наш запрос отработал правильно

Учим WrapAPI недостающим фичам

Теперь нужно указать WrapAPI, как обрабатывать полученный результат и в каком виде его представлять. Переходи на следующую вкладку — Outputs and response.

Шаг настройки постпроцессоров полученного контента

Шаг настройки постпроцессоров полученного контента

Тестовый кейс page1, ответ сервера

Тестовый кейс page1, ответ сервера

JSON output

Первым делом нужно вытащить из объекта JSON значение атрибута content. Создавай новый output типа JSON и в появившемся модальном окне указывай имя параметра content . Сразу же под текстовым полем WrapAPI подсветит найденное значение выходной строки. То, что нам нужно. Сохраняем output и идем дальше.

JSON output для получения значения атрибута content на выход

JSON output для получения значения атрибута content на выход

CSS output

Следующий шаг — вытащить нужные нам поля постов из полученной с сервера верстки, а именно title , excerpt , image , date и id .

Во WrapAPI можно создавать дочерние аутпуты. Нажав на + около существующего output, ты создашь дочерний output, который будет принимать на выход значение предыдущего. Не перепутай! Если просто выбрать пункт Add new output, то будет создан новый root-селектор, который на вход получит голый ответ сервера.

Создаем дочерний CSS output

Создаем дочерний CSS output

В появившемся окне вводим название класса заголовка .title-text . Внимание: обязательно отметь опцию Select all into an array , иначе будет выбран только первый заголовок, а нам нужно получить все десять по количеству постов в одном ответе сервера.

Задаем параметры получения данных из HTML-верстки

Задаем параметры получения данных из HTML-верстки

На выходе в ключе titles у нас окажется массив заголовков, которые вернул CSS output. Согласись, уже неплохо, и все это — без единой строки кода!

Полученные заголовки новостей

Полученные заголовки новостей

Как получить остальные параметры

Как ты помнишь, кроме title , для каждого поста нам нужно получить еще excerpt , image , date и id . Тут все не так здорово: WrapAPI имеет два ограничения:

  • он не позволяет создавать цепочки из более чем одного уровня вложенности дочерних outputs;
  • он не позволяет задавать несколько селекторов для CSS output’a. То есть CSS output может вытащить только title , только date и так далее.

Признаться, мне пришлось немного поломать голову, чтобы обойти эти ограничения. Я сделал много дочерних по отношению к JSON аутпутов CSS — по одному на каждый из параметров. Они выводят мне в итоговый результат несколько массивов: один с заголовками, один с превью статьи, один с датами и так далее.

Массив дочерних аутпутов, каждый из которых выбирает свой атрибут постов

Массив дочерних аутпутов, каждый из которых выбирает свой атрибут постов

В итоге у меня получился вот такой массив данных:

Стоит отметить, что для backgroundImages нужно указать получение не текста HTML-тега, а значения атрибута style , так как URL картинки задан в свойстве inline-CSS, а не в атрибуте src тега img .

Приводим все в порядок

Сейчас наш API уже выглядит вполне читаемым, осталось решить две проблемы:

  • все компоненты поста — заголовок, дата, превью — находятся в разных массивах;
  • в backgroundImages попал кусок CSS, а не чистый URL.

Решить эти проблемы нам поможет следующая вкладка — Post-processing script. Она позволяет написать небольшой синхронный скрипт на JavaScript, который может сделать что-то с нашим контентом перед тем, как он отправится на выход.

Скрипт должен содержать функцию postProcess() , которая принимает один аргумент — текущие результаты парсинга. То, что она вернет, и будет конечным ответом нашего API.

Я набросал небольшой скрипт, который быстро собрал все компоненты в единый массив постов, а также почистил URL картинки. Останавливаться на этом подробнее смысла нет, все, я думаю, и так предельно ясно.

Пишем скрипт для постпроцессинга данных

Пишем скрипт для постпроцессинга данных

Тестируем результат

Перед тем как пробовать наш запрос, нужно получить API-ключ. Ключи WrapAPI бывают двух типов:

  • приватные, для использования на сервере, не имеют ограничений;
  • публичные, для использования на клиенте. Они имеют ограничение по домену, с которого происходит запрос.

Для теста получи новый приватный ключ и попробуй сделать запрос к своему API, поставив свой ключ в query-параметр wrapAPIKey .

Получение ключей доступа к API

Получение ключей доступа к API

У меня вышел вот такой запрос:

Ответ сервера показан на скриншоте. Победа! 🙂

Десять постов с 256-й страницы «Хакера» (?page=256)

Десять постов с 256-й страницы «Хакера» (?page=256)

Выводы

Читайте также: