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

В этой статье мы разберем, как получить и использовать API сайта, если по нему нет документации или оно еще не открыто официально. Руководство написано для новичков, которые еще не пробовали зареверсить простой API. Для тех же кто сам занимался подобным ничего нового здесь нет.

Разбор проведем на примере API сервиса https://www.captionbot.ai/ который недавно открыл Microsoft (спасибо им за это). Многие могли прочитать о нем в статье на Geektimes. Сайт использует ajax запросы в формате JSON, поэтому скопировать их будет легко и приятно. Поехали!

КДПВ

Анализируем запросы

В первую очередь открываем инструменты разработчика и анализируем запросы, которые сайт посылает на сервер.

image

В нашем случае все интересующие нас запросы имеют базовый URL https://www.captionbot.ai/api

Инициализация

При первом открытии сайта идет GET запрос на /api/init без параметров.
Ответ имеет Content-Type: application/json, при этом в теле ответа нам приходит просто строка вида:

"54cER5HILuE"

Запомним это и идем дальше.

Отправка URL

У нас есть два способа загрузить изображение: через URL и через загрузку файла. Для теста берем URL изображения Лены с вики и отсылаем. В сетевой активности появляется POST запрос на /api/message со следующими параметрами:

{     "conversationId": "54cER5HILuE",     "waterMark": "",     "userMessage": "https://upload.wikimedia.org/wikipedia/ru/2/24/Lenna.png" }

Ага, говорим себе мы, значит метод init вернул нам строку для conversationId, а в userMessage попала наша ссылка. Что такое waterMark пока непонятно. Смотрим на данные ответа:

"{\"ConversationId\":null,\"WaterMark\":\"131071012038902294\",\"UserMessage\":\"I am not really confident, but I think it's a woman wearing a hat\\nand she seems . \",\"Status\":null}"

Зачем-то закодировали JSON дважды, ну да ладно. В человеческом виде это выглядит так:

{     "ConversationId": null,     "WaterMark": "131071012038902294",     "UserMessage": "I am not really confident, but I think it's a woman wearing a hat\\nand she seems .",     "Status": null }

Все параметры по пути поменяли манеру написания, но это мелочи жизни. Итак, нам вернули некоторое значение WaterMark, почему-то пустой ConversationId, собственно подпись к фото в поле UserMessage и некий пустой статус.

Загрузка изображения

Далее, не закрывая вкладку, пробуем ту же операцию с загрузкой фото из локального файла. Видим POST запрос на /api/upload в формате multipart/form-data с названием поля file:

-----------------------------50022246920687 Content-Disposition: form-data; name="file"; filename="Lenna.png" Content-Type: image/png

В ответ получаем строку URL нашего загруженного файла, можем перейти по нему и убедиться в этом:

"https://captionbot.blob.core.windows.net/images-container/2ogw3q4m.png"

Затем отсылается уже знакомый нам запрос на /api/message:

{     "conversationId": "54cER5HILuE",     "waterMark": "131071012038902294",     "userMessage": "https://captionbot.blob.core.windows.net/images-container/2ogw3q4m.png" }

Вот и пригодился waterMark из предыдущего ответа, а URL тот, что нам вернул метод upload. Данные ответа аналогичны предыдущим.

Пишем обертку

Чтобы использовать полученные знания с удобством, делаем простую обертку на вашем любимом языке программирования. Я сделаю это на Python. Для запросов к сайту использую requests, так как он удобный и в нем есть сессии, которые хранят cookie за меня. Сайт использует SSL, но по дефолту requests будет ругаться на сертификат:

hostname 'www.captionbot.ai' doesn't match either of '*.azurewebsites.net', '*.scm.azurewebsites.net', '*.azure-mobile.net', '*.scm.azure-mobile.net'

Решается это установкой флага verify=False при каждом запросе.

Полный исходник

import json import mimetypes import os import requests import logging logger = logging.getLogger("captionbot")  class CaptionBot:     BASE_URL = "https://www.captionbot.ai/api/"      def __init__(self):         self.session = requests.Session()         url = self.BASE_URL + "init"         resp = self.session.get(url, verify=False)         logger.debug("init: {}".format(resp))         self.conversation_id = json.loads(resp.text)         self.watermark = ''      def _upload(self, filename):         url = self.BASE_URL + "upload"         mime = mimetypes.guess_type(filename)[0]         name = os.path.basename(filename)         files = {'file': (name, open(filename, 'rb'), mime)}         resp = self.session.post(url, files=files, verify=False)         logger.debug("upload: {}".format(resp))         return json.loads(resp.text)      def url_caption(self, image_url):         data = json.dumps({             "userMessage": image_url,             "conversationId": self.conversation_id,             "waterMark": self.watermark         })         headers = {             "Content-Type": "application/json"         }         url = self.BASE_URL + "message"         resp = self.session.post(url, data=data, headers=headers, verify=False)         logger.debug("url_caption: {}".format(resp))         if not resp.ok:             return None         res = json.loads(json.loads(resp.text))         self.watermark = res.get("WaterMark")         return res.get("UserMessage")      def file_caption(self, filename):         upload_filename = self._upload(filename)         return self.url_caption(upload_filename)

Исходный код есть на Github, плюс готовый пакет в pip.

ссылка на оригинал статьи https://habrahabr.ru/post/283112/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *