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

Суть: парсер циан. Нашёл API циан, но там передаются не все данные. Решил не трогать API и парсить саму страничку, но прокси быстро улетают в бан, хотя задержка большая стоит. После этого решил покопаться в консоли разработчика и увидел в Response следующую картину: Как я понял скрипт //cdn.cian.site/frontend/header-frontend/main.289cc52627b5bd8057f8.js генерирует страничку и делает XMLHttpRequest запросы внутри. Как мне определить и взять API из этого файла? Изменять я его не могу т.к. он сервере. Буду очень благодарен за ответ.
Отслеживать
задан 31 янв 2022 в 14:18
65 7 7 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Никак. Это все живёт в рамках сессии. Сгенерился токен, с ним вот такой js запросы вне этого js работать не будут.
Я Апи маркета юзал через проки. При бане прокси она автоматом улетала в валгаллу. Но надо постоянно подкидывать прокси в топку
Отслеживать
ответ дан 31 янв 2022 в 14:40
Yuriy Belalov Yuriy Belalov
79 2 2 бронзовых знака
- javascript
- python
- парсер
- api
- rest
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.1.3.2953
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Как использовать 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-формат здесь использован для удобства.
Третьим опциональным параметром могут идти любые дополнительные параметры запроса, 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"
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(), то в функцию будет передан объект, а не строка. Это можно использовать в своих целях при необходимости.
Upd: в версии 1.2.0 добавилась возможность инициализировать апи из yaml файла, подробнее можно посмотреть в документации.
Обнаружение REST API
Как правило все виды обнаружения сводятся к чтению схемы маршрута или схемы всего WP API, но чтобы можно было прочитать такую схему нужно выяснить (обнаружить) какой у REST API корневой URL. В PHP получить корневой URL можно с помощью функции rest_url().
Оглавление:
- Обнаружение через Link в заголовке ответа
- Обнаружение через метатег
- Обнаружение через RSD
- Обнаружение способов Авторизации
- Обнаружение имеющихся Расширений
Обнаружение через Link в заголовке ответа
Рекомендуемый путь обнаружить URL на REST API это отправить HEAD запрос на любую страницу сайта и проверить есть ли в ответе параметр Link: . Rest API автоматически добавляет параметр Link в заголовки ответа для всех страниц во фронтэнде.
Link: ; rel="https://api.w.org/"
Указанная в параметре ссылка ведет на корневой маршрут REST API ( / ). Его можно использовать для дальнейшего обнаружения маршрутов.
Для сайтов с отключенными ЧПУ /wp-json/ автоматически не обрабатывается в WordPress. И в таком случае ссылка на REST API будет выглядеть так:
Link: ; rel="https://api.w.org/"
Обнаружение через метатег
Для клиентов которые не умеют читать HEADER заголовки ответа (которые парсят HTML или запускаются в браузере), URL на REST API можно получить в HTML метатеге link в части документа. Такой метатег также добавляется на всех страницах во вонтэнде.
В Javascript эту ссылку можно получить через DOM:
// jQuery вариант var api_root = jQuery( 'link[rel="https://api.w.org/"]' ).attr( 'href' ); // Нативный JS var links = document.getElementsByTagName( 'link' ); var link = Array.prototype.filter.call( links, function ( item ) < return item.rel === 'https://api.w.org/'; >); var api_root = link[0].href;
Такое обнаружение также справедливо для обнаружения Atom/RSS фидов. Таким образом этот код можно адаптировать пот эту потребность.
Обнаружение через RSD
Для клиентов с поддержкой XML-RPC обнаружения, возможно удобнее будет получить ссылку на REST API через XML. Тут нужно сделать два шага:
Первый шаг: найти конечную точку (URL) RSD, она расположена также в элементе в части на любой странице фронтэнда:
Второй шаг: получить XML код по обнаруженной ссылке и распарсить его. Выглядит он примерно так:
WordPress https://wordpress.org/ http://example.com/
Элемент со ссылкой на REST API всегда будет иметь атрибут name=»WP-API» .
RDS обнаружение это НЕ рекомендуемый способ, потому что он наиболее сложный, тут нужно сначала найти ссылку на RDS затем распарсить код по этой ссылки и только потом получить URL самого REST API.
По возможности рекомендуется избегать такого RDS обнаружения!
Обнаружение способов Авторизации
Обнаружение также позволяет узнать какие методы аутентификации имеются в REST API. Ответ главного маршрута REST API /wp-json/ содержит объект, который полностью описывает API. В этом объекте под ключом authentication находятся данные о возможных способах авторизации в REST API:
< "name": "Example WordPress Site", "description": "YOLO", "routes": < . >, "authentication": < "oauth1": < "request": "http://example.com/oauth/request", "authorize": "http://example.com/oauth/authorize", "access": "http://example.com/oauth/access", "version": "0.1" >> >
Подробнее про аутентификацию в REST API читайте в соответствующем разделе.
Обнаружение имеющихся Расширений
После того, как REST API обнаружен, нужно узнать что API поддерживает. Узнать какие расширения есть в API можно из ответа на корневой маршрут REST API /wp-json/ . Там под ключом namespaces находятся все имеющиеся расширения API:
Для WP версий от 4.4 до 4.6, доступны только базовое расширение oEmbed (полное API описанное в этом руководстве еще недоступно).
С версии WP 4.7 доступно уже все API, так можно видеть новое расширение wp/v2 :
Прежде чем пытаться использовать любую из конечных точек, нужно убедиться, что она поддерживается в API, для этого нужно проверить наличие нужного пространства имен, например wp/v2 .
WordPress 4.4 включает инфраструктуру API для всех сайтов, но не включает основные конечные точки под wp/v2 . Конечные точки ядра работают с версии WordPress 4.7.
Этот же механизм можно использовать для определения поддержки REST расширений у плагинов. Например, представим что плагин регистрирует следующий маршрут:
register_rest_route( 'testplugin/v1', '/testroute', array( /* . */ ) );
Тогда в данных API появится новое расширение testplugin/v1 :
Доступные атлетические скамьи Здесь доступные атлетические скамьи, наклонные для дома и спортивного зала, от производителя. www.all4gym.ru
Комментариев нет
Навигация по учебнику
- Шпаргалка
- Знакомство с WordPress
- Кодекс
- REST API
- Базовые понятия
- Глобальные параметры запроса
- Авторизация
- Пароли приложений
- Basic
- Создание Маршрутов
- Маршруты для постов и таксономий
- Типы параметров и Валидация
- Классы контроллеров
- Создание Схемы Маршрута
- Изменение ответов рабочих эндпоинтов
- Типы записей (types)
- Записи (posts)
- Страницы (pages)
- Ревизии (revisions)
- Медиа (media)
- Произвольный Тип записи (post_type)
- Статусы записей (statuses)
- Комментарии (comments)
- Таксономии (taxonomies)
- Категории (categories)
- Теги (tags)
- Произвольный Термин (Term)
- Пользователи (users)
- Настройки (settings, options)
- Темы (themes)
- Поиск (search)
- Блоки (blocks)
Поиск открытого API сайта или Ускоряем парсинг в 10 раз
Цель статьи — описать алгоритм действий поиска открытого API сайта.
Целевая аудитория статьи — программисты, которым интересен парсинг и анализ уязвимостей сайтов.В статье рассмотрим пример поиска API сайта edadeal.ru, познакомимся с протоколом google protobuf и сравним скорость различных подходов парсинга
1. Введение
Парсинг (в контексте статьи) — это автоматизированный процесс извлечение данных из Интернета.
Существует 2 подхода к извлечению данных со страниц сайта
- Извлекать данные из HTML-кода страницы сайта
Плюсы — этот способ прост и работает всегда, так как код страницы всегда доступен пользователю
Минусы — этот способ может работать долго (несколько секунд), если часть данных генерирует java script (например, данные появляются только после прокручивания страницы или нажатия кнопки) - Использовать API сайта
Плюсы — быстрее первого способа и не зависит от изменений структуры html-страницы
Минус — не у всех сайтов есть открытое API
В статье рассмотрим пример поиска API сайта edadeal.ru, познакомимся с протоколом google protobuf и сравним скорость двух подходов парсинга
2. Постановка задачи
Задача — извлечь данные о продуктах с сайта Едадил (название продукта, цена, размер скидки, магазин, город и т.д)
3. Решение

1 Делаем запрос к странице, которую мы хотим парсить.
2 Перебираем все запросы, которые делает сайт. Для этого используем DevTools браузера3 Анализируем запросы
Из названия запроса понимаем, что нам нужен запрос
https://squark.edadeal.ru/web/search/offers?count=30&locality=moskva&page=1&retailer=5kaВ ответ на запрос получаем файл (назовем его binary_file.bin). Как узнать кодировку этого файла?
Формат файла из пункта 3 нам подсказывает строка-хедер content-type: application/x-protobuf4 Определим структуру данных (.proto файл)
с помощью утилиты protoc (http://google.github.io/proto-lens/installing-protoc.html) преобразуем закодированный файл в понятный человеку формат
protoc —decode_raw < binary_file.binПолучаем список словарей:
1 < 1: "e\341_\260\007\177W\202\222O\326\316\233\326\000A" 2: "\320\242\321\203\320\260\320\273\320\265\321\202\320\275\320\260\321\217 \320\261\321\203\320\274\320\260\320\263\320\260 Familia Plus, 2 \321\201\320\273\320\276\321\217, 12 \321\200\321\203\320\273\320\276\320\275\320\276\320\262, 1 \321\203\320\277." 3: "https://leonardo.edadeal.io/dyn/cr/catalyst/offers/u4nf6zbkjc3m5lss46ucvxjafm.jpg" 4: 0x43ad7eb8 5: 0x4347e666 7: ";5\332^c\021\021\346\204\237RT\000\020\266\010" 8: 0x41400000 9: "\321\210\321\202" 10: 0x422c0000 11: "%" 13: 43 15: "2022-07-26T00:00:00Z" 16: "2022-08-01T00:00:00Z" 19: "A1\005L\332nPg\230\342q\375\031\335\014\336" 20 < 1: 0x3f800000 2: 0x418547ae 3: "\321\210\321\202" 4: 1 >21: "\224\331\203\202B\303\021\346\224\031RT\000\020\266\010" 22: "K3\020\2537
5 Формируем .proto файл
Используем номера из предыдущего пункта, по контенту из предыдущего пункта нужно догадаться, какие поля, что означают (например 3 — это ссылка на изображение продукта)Методом проб и ошибок получаем следующую структуру:
syntax = "proto2"; message Offers < repeated Offer offer = 1; >message Offer
4 Переходим к написанию кода
Создаем питоновский файл с описанием структуры из .proto файла
protoc —proto_path=proto_files —python_out=proto_structs offers.protoproto_files — имя директории с .proto файлами
proto_structs — в этой директории сохраняются результаты (_pb2.py файлы)Код работает следующим образом:
- Делает запрос к API сайта
- Преобразует ответ сайта в json
- Выводит результат
import json import requests from google.protobuf.json_format import MessageToJson from proto_structs import offers_pb2 def parse_page(city = "moskva", shop = "5ka", page_num = 1): """ :param city: location of the shop :param shop: shop name :param page_num: parsed page number :return: None """ url = f"https://squark.edadeal.ru/web/search/offers?count=30&locality=&page=&retailer=" data = requests.get(url, allow_redirects=True) # data.content is a protobuf message offers = offers_pb2.Offers() # protobuf structure offers.ParseFromString(data.content) # parse binary data products: str = MessageToJson(offers) # convert protobuf message to json products = json.loads(products) print(json.dumps(products, indent=4, ensure_ascii=False,)) if __name__ == "__main__": parse_page()Результат работы программы — список продуктов с описанием
5 Сравним результаты
Время выполнения кода из предыдущего пункта 0.3 — 0.4 секунды
Альтернативный вариант парсинга — загрузка всего html-кода страницы и извлечения нужной информации из этого кодаfrom selenium import webdriver driver = webdriver.Chrome() driver.get("https://edadeal.ru/moskva/retailers/5ka") # извлечение данных из html кодаВремя полной загрузки страницы 5 — 6 секунд.
6 Выводы
Лучше использовать API сайта для извлечения данных, если есть такая возможность
Использование API сайта позволяет не зависеть от изменений в html-коде страниы
- Базовые понятия