Часто возникает задача периодически парсить какой-нибудь сайт на наличие новой информации. Например, если ты пишешь агрегатор контента с новостного сайта или форума, в котором нет поддержки RSS. Проще всего написать скрепер на Питоне и разобрать полученный HTML через beautifulsoup или регулярками. Однако есть более элегантный способ — самому сделать недостающие API для сайта и получать ответы в привычном JSON, как будто бы у сайта есть нативный API.
Не будем далеко ходить за примером и напишем парсер контента с сайта известного журнала. Как ты знаешь, сайты не сейчас не предоставляет никакого API для программного получения статей, кроме RSS. Однако RSS не всегда удобен, да и выдает далеко не всю нужную информацию. Исправим это!
ПОСТАНОВКА ЗАДАЧИ
Итак, наша задача: сделать API вида GET /posts, который бы отдавал десять последних статей в JSON. Также нам нужно иметь возможность задавать сдвиг, то есть раз за разом получать следующие десять постов.
GET /posts
Ответ должен быть таким:
Также нужно иметь возможность получать следующие десять постов — со второй страницы, третьей и так далее. Это делается через GET-параметр вида GET /posts?page=2. Если page в запросе не указан, считаем его равным 1 и отдаем посты с первой страницы «Хакера». В общем, задача ясна, переходим к решению.
ФРЕЙМВОРК ДЛЯ ПАРСЕРА WEB САЙТОВ
WrapAPI — это довольно новый (пара месяцев от роду) сервис для построения мощных кастомных парсеров веба и предоставления к ним доступа по API. Не пугайся, если ничего не понял, сейчас поясню на пальцах. Работает так:
- Указываешь WrapAPI страницу, которую нужно парсить (в нашем случае главную «Хакера» — https://xakep.ru/).
- Говоришь, с какими параметрами обращаться к серверу, каким HTTP-методом (GET или POST), какие query-параметры передавать, какие POST-параметры в body, куки, хедеры. Короче, все, что нужно, чтобы сервер вернул тебе нормальную страничку и ничего не заподозрил.
- Указываешь WrapAPI, где на полученной странице ценный контент, который надо вытащить, в каком виде его представлять.
- Получаешь готовый URL для API вида GET /posts, который вернет тебе все выдранные с главной «Хакера» посты в удобном JSON!
Немного о приватности запросов
Ты наверняка уже задумался о том, насколько безопасно использовать чужой сервис и передавать ему параметры своих запросов с приватными данными. Тем более что по умолчанию для каждого нового API-проекта будет создаваться публичный репозиторий и запускать API из него сможет любой желающий. Не все так плохо:
1. Каждый API-репозиторий (а соответственно, и все API-запросы в нем) можно сделать приватным. Они не будут показываться в общем списке уже созданных API на платформе WrapAPI. Просто выбери достаточно сложное имя репозитория, и шанс, что на него кто-то забредет случайно, сведется к минимуму.
2. Любой запрос к WrapAPI требует специального токена, который нужно получить в своей учетке WrapAPI. То есть просто так узнать URL к твоему репозиторию и таскать через
ПРИГОТОВЛЕНИЯ
Несколько простых шагов перед началом.
1. Идем на сайт WrapAPI, создаем новую учетку и логинимся в нее.
2. Устанавливаем расширение для Chrome (подойдет любой Chromium-based браузер), открываем консоль разработчика и видим новую вкладку WrapAPI. 3. Переходим на нее и логинимся.
Это расширение нам понадобится для того, чтобы перехватывать запросы, которые мы собираемся эмулировать, и быстро направлять их в WrapAPI для дальнейшей работы. По логике работы это расширение очень похоже на связку Burp Proxy + Burp Intruder.
ОТЛАВЛИВАЕМ ЗАПРОСЫ
Теперь нужно указать WrapAPI, какой HTTP-запрос мы будем использовать для построения нашего API. Идем на сайт «Хакера» и открываем консоль разработчика, переключившись на вкладку WrapAPI.
Для получения постов я предлагаю использовать запрос пагинации, он доступен без авторизации и может отдавать по десять постов для любой страницы «Хакера», возвращая HTML в объекте JSON (см. ниже).
Чтобы WrapAPI начал перехватывать запросы, нажми Start capturing requests и после этого выполни целевой запрос (на пагинацию). Плагин поймает POST-запрос к странице https://xakep.ru/wp-admin/admin-ajax.php с кучей form/urlencoded-параметров в теле, в том числе и номером страницы. Ответом на запрос будет JSON-объект с параметром content, содержащий закешированный HTML-код с новыми постами. Собственно, этот блок и нужно парсить WrapAPI.
КОНФИГУРИРУЕМ WRAPAPI
После того как ты выбрал нужное имя для твоего репозитория (я взял test001 и endpoint posts) и сохранил его на сервер WrapAPI через расширение для Chrome, иди на сайт WrapAPI и открывай репозиторий. Самое время настраивать наш API.
Переходи на вкладку Inputs and request. Здесь нам понадобится указать, с какими параметрами WrapAPI должен парсить запрашиваемую страницу, чтобы сервер отдал ему валидный ответ.
Аккуратно перебей все параметры из пойманной WrapAPI полезной нагрузки (POST body payload) в поле слева. Для всех параметров, кроме paginated, выставь тип Constant. Это означает, что в запросы к серверу будут поставляться предопределенные значения, управлять которыми мы не сможем (нам это и не нужно). А вот для paginated выставляй Variable API, указав имя page. Это позволит нам потом обращаться к нашему API по URL вида GET / posts?page=5 (с query-параметром page), а на сервер уже будет уходить полноценный POST со всеми перечисленными параметрами.
[ad name=»Responbl»]
Заголовки запроса ниже можно не трогать, я использовал стандартные из Chromium. Если парсишь не «Хакер», а данные с какого-нибудь закрытого сервера, можешь подставить туда нужные куки, хедеры, basic-auth и все, что нужно. Одним словом, ты сможешь настроить свой запрос так, чтобы сервер безо всяких подозрений отдал тебе контент.
УЧИМ WRAPAPI НЕДОСТАЮЩИМ ФИЧАМ
Теперь нужно указать WrapAPI, как обрабатывать полученный результат и в каком виде его представлять. Переходи на следующую вкладку — Outputs and response.
Небольшой глоссарий, прежде чем идти дальше:
- Output — фильтр-постпроцессор контента, который принимает на входесырой ответ сервера, а возвращает уже модифицированный по заданным правилам. Они бывают нескольких типов. Самые часто используемые:
- JSON выбирает содержимое указанного атрибута, который подан на вход JSON-объекта, и возвращает его значение как строку;
- CSS выбирает элементы DOM по указанному CSS-селектору (например, ID или классу) и возвращает их значение, атрибут или весь HTML-тег целиком. Может вернуть как одну строку, так и массив найденных вхождений;
- Regular expression выбирает вхождения по регулярному выражению, в остальном то же, что и предыдущий output;
- HTTP Header выбирает значение HTTP-заголовка ответа сервера и возвращает его строкой;
- Cookie выбирает значение Cookie, полученной в ответе от сервера, и возвращает его строкой.
- Output Scenario — набор аутпутов, которые объединены в одну или несколько параллельных цепочек. По сути — почти весь набор препроцессоров, которые превращают серверный ответ в нужный нам формат.
- Test case — сохраненный ответ сервера, на котором тестируются обработчики и подбирается нужная цепочка аутпутов.
Создай новый test case, сохрани его под именем page1. Теперь посмотри, что вернул сервер. Это должен быть объект JSON, одно из полей которого содержит кусок HTTP-разметки с перечислением запрошенных постов.
Продолжение следует..
1 comments On Пример парсера для web сайтов.
Pingback: Пишем парсер для веб сайта. Продолжение - Cryptoworld ()