Как использовать API сайта, у которого нет API?

У меня достаточно часто появляется задача получить данные от стороннего сайта, при этом далеко не всегда этот сайт предоставляет возможность удобно получить эти данные через API. Единственное решение в таком случае — парсить html содержимое страниц. Когда-то я писал регэкспы, потом появились библиотеки, позволяющие получить нужное содержимое по css-селектору, а сейчас и это кажется сложной задачей, которую хотелось бы упростить.Сегодня я хочу рассказать вам о моей небольшой библиотеке, позволяющей описать в API-стиле http-запросы и парсить ответ сервера в нужный вам формат.Примечание: не стоит забывать об авторских правах, если вы используете чужие данные.

УстановкаБиблиотека доступна к установке через composer, поэтому все, что необходимо сделать — это добавить зависимость «sleeping-owl/apist»:»1.*» в ваш composer.json и вызвать composer update.У данной библиотеки нет зависимостей от каких-либо фреймворков, поэтому вы можете использовать ее с любым фреймворком, либо же в чистом PHP-проекте. Для сетевых запросов используется Guzzle, для манипуляций с dom-деревом используется «symfony/dom-crawler».

Использование После установки вы можете приступить к созданию нового класса, олицетворяющего API нужного вам сайта. Библиотека не накладывает никаких ограничений на то, как и где вы будете создавать свой класс. Нужно расширить класс SleepingOwl\Apist\Apist и указать базовый урл: use SleepingOwl\Apist\Apist;

class HabrApi extends Apist { protected $baseUrl = 'http://habrahabr.ru'; } Это все, что нужно для базового описания. Далее вы можете добавлять в данный класс методы, которые вам нужны: public function index () { return $this→get ('/', [ 'title' => Apist: filter ('.page_head .title')→text ()→trim (), 'posts' => Apist: filter ('.posts .post')→each ([ 'title' => Apist: filter ('h1.title a')→text (), 'link' => Apist: filter ('h1.title a')→attr ('href'), 'hubs' => Apist: filter ('.hubs a')→each (Apist: filter ('*')→text ()), 'author' => [ 'username' => Apist: filter ('.author a'), 'profile_link' => Apist: filter ('.author a')→attr ('href'), 'rating' => Apist: filter ('.author .rating')→text () ] ]) ]); } Здесь метод «get» — это тип используемого http-запроса, также доступны остальные методы (post, put, patch, delete и т.д.).Первый параметр — урл данного метода, он может быть как относительным, так и абсолютным.Второй параметр — это и есть та основа, из-за которой я создал эту библиотеку. Он описывает структуру, которую необходимо получить в результате вызова данного метода. Это может быть как массив, так и одиночное значение. То есть для описанного выше метода результат будет такого вида: $api = new HabrApi; $result = $api→index (); Примечание: результат будет типа array, json-формат здесь использован для удобства. { «title»: «Публикации», «posts»: [ { «title»: «Проверьте своего хостера на уязвимость Shellshock (часть 2)», «link»: «http:\/\/habrahabr.ru\/company\/host-tracker\/blog\/240389\/», «hubs»: [ «Блог компании ХостТрекер», «Серверное администрирование», «Информационная безопасность» ], «author»: { «username»: «smiHT», «profile_link»: «http:\/\/habrahabr.ru\/users\/smiHT\/», «rating»:»26,9» } }, { «title»: «Курсы этичного хакинга и тестирования на проникновение от PentestIT», «link»: «http:\/\/habrahabr.ru\/company\/pentestit\/blog\/240995\/», «hubs»: [ «Блог компании PentestIT», «Учебный процесс в IT», «Информационная безопасность» ], «author»: { «username»: «pentestit-team», «profile_link»: «http:\/\/habrahabr.ru\/users\/pentestit-team\/», «rating»:»36,4» } }, … ] } Третьим опциональным параметром могут идти любые дополнительные параметры запроса, get или post переменные, загружаемые файлы, заголовки запроса и т.п. С полным списком можно ознакомиться в документации Guzzle.Создание фильтров Пара слов о том, как это работает: каждый объект, созданный через Apist: filter ($cssSelector) после загрузки данных заменяется на нужное значение, он сохраняет не только сам селектор, по которому он будет искать данные, но и всю вереницу вызовов, которые к нему были применены. После загрузки данных он пытается применить эти методы к найденным элементам.Вот некоторые типы методов, которые могут быть применены (вы можете комбинировать их в нужной вам последовательности):

Методы класса Symfony\Component\DomCrawler\Crawler для перемещения по dom-дереву и получению данных: Apist: filter ('.navbar li')→eq (3)→filter ('a.active')→text (); Apist: filter ('input')→first ()→attr ('value'); Apist: filter ('.content')→html (); Созданные мной методы: Apist: filter ('body')→element (); // Вернет объект класса Symfony\Component\DomCrawler\Crawler, отвечающий за элемент body

Apist: filter ('.post')→each (…); // Этот объект будет заменен на массив, каждый элемент которого будет создан согласно схеме, которая была передана параметром. Все внутренние css-селекторы будут применены относительно текущего элемента.

Apist: filter ('.errors')→exists ()→then (…)→else (…); // Описывает условие, если элемент с классом «errors» был найден, то используется значение из блока «then», иначе из блока «else» PHP-функции или ваши функции, описанные в корневом namespace. При этом текущий элемент будет передан в качестве первого параметра, а остальными параметрами будут те, что вы указали при инициализации. Apist: filter ('.title')→text ()→mb_strtoupper ()→trim ()→substr (5);

function myFunc ($string, $find, $replace) { return str_replace ($find, $replace, $string); } Apist: filter ('.title')→text ()→myFunc ('My', 'Your'); // Если убрать →text (), то в функцию будет передан объект, а не строка. Это можно использовать в своих целях при необходимости. Исходники демо-класса HabrApi.php, используемого в примерах на сайте проекта можно посмотреть здесь.Исходники на GitHub | Документация и примеры

© Habrahabr.ru