Поиск открытого 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{O\271\273\374K\351\376\224\310*" 22: "\300\336d(\224kL\025\224\300\355\256\247\327R\035" 22: "\303O:\202\330\262A\326\246\023\307D\314F\303G" 22: "\210\"\022?\250|L.\272\375\345{\335c,\026" 22: "=3yP\026\004N\334\267\377\320\036F\326\331\\" 22: "E\211\000\246e6EI\223\000)\242\3348\216M" 22: "V#\263\022\367\324H\350\232r\013\010_KX\273" 23: "\320\232\320\276\320\273\320\270\321\207\320\265\321\201\321\202\320\262\320\276" 24: 1 }
5 Формируем .proto файл
Используем номера из предыдущего пункта, по контенту из предыдущего пункта нужно догадаться, какие поля, что означают (например 3 — это ссылка на изображение продукта)
Методом проб и ошибок получаем следующую структуру:
syntax = "proto2"; message Offers { repeated Offer offer = 1; } message Offer { optional string name = 2; optional string image_url = 3; optional float price_before = 4; optional float price_after = 5; optional float amount = 8; optional float discount = 10; optional string start_date = 15; optional string end_date = 16; }
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={city}&page={page_num}&retailer={shop}" 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()
Результат работы программы — список продуктов с описанием
{ "offer": [ { "name": "Наггетсы, куриные с ветчиной, Мираторг, 300 г", "imageUrl": "https://leonardo.edadeal.io/dyn/cr/catalyst/offers/necnmkv43splbm3hr5636snpry.jpg", "priceBefore": 218.99000549316406, "priceAfter": 109.48999786376953, "amount": 300.0, "discount": 51.0, "startDate": "2022-08-02T00:00:00Z", "endDate": "2022-08-08T00:00:00Z" }, ...
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-коде страниы
ссылка на оригинал статьи https://habr.com/ru/post/680320/
Добавить комментарий