Как можно получить скрытый API сайта
Суть: парсер циан. Нашёл API циан, но там передаются не все данные. Решил не трогать API и парсить саму страничку, но прокси быстро улетают в бан, хотя задержка большая стоит. После этого решил покопаться в консоли разработчика и увидел в Response следующую картину: Как я понял скрипт //cdn.cian.site/frontend/header-frontend/main.289cc52627b5bd8057f8.js генерирует страничку и делает XMLHttpRequest запросы внутри. Как мне определить и взять API из этого файла? Изменять я его не могу т.к. он сервере. Буду очень благодарен за ответ.
Отслеживать
задан 31 янв 2022 в 14:18
75 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.4.30.8420
Поиск открытого 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-protobuf
4 Определим структуру данных (.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.proto
proto_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-коде страниы
Как спарсить любой сайт?
Меня зовут Даниил Охлопков, и я расскажу про свой подход к написанию скриптов, извлекающих данные из интернета: с чего начать, куда смотреть и что использовать.
Написав тонну парсеров, я придумал алгоритм действий, который не только минимизирует затраченное время на разработку, но и увеличивает их живучесть, робастность, масштабируемость.
TL;DR
Чтобы спарсить данные с вебсайта, пробуйте подходы именно в таком порядке:
- Найдите официальное API,
- Найдите XHR запросы в консоли разработчика вашего браузера,
- Найдите сырые JSON в html странице,
- Отрендерите код страницы через автоматизацию браузера,
- Если ничего не подошло — пишите парсеры HTML кода.
Совет профессионалов: не начинайте с BS4/Scrapy
BeautifulSoup4 и Scrapy — популярные инструменты парсинга HTML страниц (и не только!) для Python.
Крутые вебсайты с крутыми продактами делают тонну A/B тестов, чтобы повышать конверсии, вовлеченности и другие бизнес-метрики. Для нас это значит одно: элементы на вебстранице будут меняться и переставляться. В идеальном мире, наш написанный парсер не должен требовать доработки каждую неделю из-за изменений на сайте.
Приходим к выводу, что не надо извлекать данные из HTML тегов раньше времени: разметка страницы может сильно поменяться, а CSS-селекторы и XPath могут не помочь. Используйте другие методы, о которых ниже. ⬇️
Используйте официальный API
Ого? Это не очевидно ? Конечно, очевидно! Но сколько раз было: сидите пилите парсер сайта, а потом БАЦ — нашли поддержку древней RSS-ленты, обширный sitemap.xml или другие интерфейсы для разработчиков. Становится обидно, что поленились и потратили время не туда. Даже если API платный, иногда дешевле договориться с владельцами сайта, чем тратить время на разработку и поддержку.
Sitemap.xml — список страниц сайта, которые точно нужно проиндексировать гуглу. Полезно, если нужно найти все объекты на сайте. Пример: http://techcrunch.com/sitemap.xml
RSS-лента — API, который выдает вам последние посты или новости с сайта. Было раньше популярно, сейчас все реже, но где-то еще есть! Пример: https://habr.com/ru/rss/hubs/all/
Поищите XHR запросы в консоли разработчика
Все современные вебсайты (но не в дарк вебе, лол) используют Javascript, чтобы догружать данные с бекенда. Это позволяет сайтам открываться плавно и скачивать контент постепенно после получения структуры страницы (HTML, скелетон страницы).
Обычно, эти данные запрашиваются джаваскриптом через простые GET/POST запросы. А значит, можно подсмотреть эти запросы, их параметры и заголовки — а потом повторить их у себя в коде! Это делается через консоль разработчика вашего браузера (developer tools).
В итоге, даже не имея официального API, можно воспользоваться красивым и удобным закрытым API. ☺️
Даже если фронт поменяется полностью, этот API с большой вероятностью будет работать. Да, добавятся новые поля, да, возможно, некоторые данные уберут из выдачи. Но структура ответа останется, а значит, ваш парсер почти не изменится.
Алгорим действий такой:
- Открывайте вебстраницу, которую хотите спарсить
- Правой кнопкой -> Inspect (или открыть dev tools как на скрине выше)
- Открывайте вкладку Network и кликайте на фильтр XHR запросов
- Обновляйте страницу, чтобы в логах стали появляться запросы
- Найдите запрос, который запрашивает данные, которые вам нужны
- Копируйте запрос как cURL и переносите его в свой язык программирования для дальнейшей автоматизации.
Вы заметите, что иногда эти XHR запросы включают в себя огромные строки — токены, куки, сессии, которые генерируются фронтендом или бекендом. Не тратьте время на ревёрс фронта, чтобы научить свой парсер генерировать их тоже.
Вместо этого попробуйте просто скопипастить и захардкодить их в своем парсере: очень часто эти строчки валидны 7-30 дней, что может быть окей для ваших задач, а иногда и вообще несколько лет. Или поищите другие XHR запросы, в ответе которых бекенд присылает эти строчки на фронт (обычно это происходит в момент логина на сайт). Если не получилось и без куки/сессий никак, — советую переходить на автоматизацию браузера (Selenium, Puppeteer, Splash — Headless browsers) — об этом ниже.
Поищите JSON в HTML коде страницы
Как было удобно с XHR запросами, да? Ощущение, что ты используешь официальное API. Приходит много данных, ты все сохраняешь в базу. Ты счастлив. Ты бог парсинга.
Но тут надо парсить другой сайт, а там нет нужных GET/POST запросов! Ну вот нет и все. И ты думаешь: неужели расчехлять XPath/CSS-selectors? ♀️ Нет! ♂️
Чтобы страница хорошо проиндексировалась поисковиками, необходимо, чтобы в HTML коде уже содержалась вся полезная информация: поисковики не рендерят Javascript, довольствуясь только HTML. А значит, где-то в коде должны быть все данные.
Современные SSR-движки (server-side-rendering) оставляют внизу страницы JSON со всеми данные, добавленный бекендом при генерации страницы. Стоп, это же и есть ответ API, который нам нужен!
Вот несколько примеров, где такой клад может быть зарыт (не баньте, плиз):
Алгоритм действий такой:
- В dev tools берете самый первый запрос, где браузер запрашивает HTML страницу (не код текущий уже отрендеренной страницы, а именно ответ GET запроса).
- Внизу ищите длинную длинную строчку с данными.
- Если нашли — повторяете у себя в парсере этот GET запрос страницы (без рендеринга headless браузерами). Просто requests.get .
- Вырезаете JSON из HTML любыми костылямии (я использую html.find(«= <") ).
Отрендерите JS через Headless Browsers
Если XHR запросы требуют актуальных tokens, sessions, cookies. Если вы нарываетесь на защиту Cloudflare. Если вам обязательно нужно логиниться на сайте. Если вы просто решили рендерить все, что движется загружается, чтобы минимизировать вероятность бана. Во всех случаях — добро пожаловать в мир автоматизации браузеров!
Если коротко, то есть инструменты, которые позволяют управлять браузером: открывать страницы, вводить текст, скроллить, кликать. Конечно же, это все было сделано для того, чтобы автоматизировать тесты веб интерфейса. I’m something of a web QA myself.
После того, как вы открыли страницу, чуть подождали (пока JS сделает все свои 100500 запросов), можно смотреть на HTML страницу опять и поискать там тот заветный JSON со всеми данными.
driver.get(url_to_open) html = driver.page_source
Selenoid — open-source remote Selenium cluster
Для масштабируемости и простоты, я советую использовать удалённые браузерные кластеры (remote Selenium grid).
Недавно я нашел офигенный опенсорсный микросервис Selenoid, который по факту позволяет вам запускать браузеры не у себя на компе, а на удаленном сервере, подключаясь к нему по API. Несмотря на то, что Support team у них состоит из токсичных разработчиков, их микросервис довольно просто развернуть (советую это делать под VPN, так как по умолчанию никакой authentication в сервис не встроено). Я запускаю их сервис через DigitalOcean 1-Click apps: 1 клик — и у вас уже создался сервер, на котором настроен и запущен кластер Headless браузеров, готовых запускать джаваскрипт!
Вот так я подключаюсь к Selenoid из своего кода: по факту нужно просто указать адрес запущенного Selenoid, но я еще зачем-то передаю кучу параметров бразеру, вдруг вы тоже захотите. На выходе этой функции у меня обычный Selenium driver, который я использую также, как если бы я запускал браузер локально (через файлик chromedriver).
def get_selenoid_driver( enable_vnc=False, browser_name="firefox" ): capabilities = < "browserName": browser_name, "version": "", "enableVNC": enable_vnc, "enableVideo": False, "screenResolution": "1280x1024x24", "sessionTimeout": "3m", # Someone used these params too, let's have them as well "goog:chromeOptions": , "prefs": < "credentials_enable_service": False, "profile.password_manager_enabled": False >, > driver = webdriver.Remote( command_executor=SELENOID_URL, desired_capabilities=capabilities, ) driver.implicitly_wait(10) # wait for the page load no matter what if enable_vnc: print(f"You can view VNC here: ") return driver
Заметьте фложок enableVNC . Верно, вы сможете смотреть видосик с тем, что происходит на удалённом браузере. Всегда приятно наблюдать, как ваш скрипт самостоятельно логинится в Linkedin: он такой молодой, но уже хочет познакомиться с крутыми разработчиками.
Парсите HTML теги
Мой единственный совет: постараться минимизировать число фильтров и условий, чтобы меньше переобучаться на текущей структуре HTML страницы, которая может измениться в следующем A/B тесте.
Даниил Охлопков — Data Lead @ Runa Capital
Подписывайтесь на мой Телеграм канал, где я рассказываю свои истории из парсинга и сливаю датасеты.
Надеюсь, что-то из этого было полезно! Я считаю, что в парсинге важно, с чего ты начинаешь. С чего начать — я рассказал, а дальше ваш ход
Обнаружение 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
- Базовые понятия
- Глобальные параметры запроса
- Авторизация
- Пароли приложений
- По паролю юзера
- Создание Маршрутов
- Маршруты для постов и таксономий
- Типы параметров и Валидация
- Классы контроллеров
- Создание Схемы Маршрута
- Изменение ответов рабочих эндпоинтов
- Типы записей (types)
- Записи (posts)
- Страницы (pages)
- Ревизии (revisions)
- Медиа (media)
- Произвольный Тип записи (post_type)
- Статусы записей (statuses)
- Комментарии (comments)
- Таксономии (taxonomies)
- Категории (categories)
- Теги (tags)
- Произвольный Термин (Term)
- Пользователи (users)
- Настройки (settings, options)
- Темы (themes)
- Поиск (search)
- Блоки (blocks)
- Batch
- Базовые понятия