Instagram api на минималках

image

Все началось с того, что я захотел сделать канал в инстаграме потратив на поиски и тесты сервисов авто публикаций целый день, далее я решил посмотреть на готовые пакеты гитхаба я удивился на размер кода этих пакетов (некоторые фреймворки php меньше чем эти обертки над инстаграмом), я плюнул и решил написать свою обертку с минимальными возможностями.

Авторизация


Авторизацию будем проходить через WEB версию. Для этого нам понадобится с начало получить заголовки, куки.

/** Отправляем GET запрос на  https://www.instagram.com**/
 $curl = curl_init();
 curl_setopt_array($curl, [
        CURLOPT_URL => 'https://www.instagram.com/',
        CURLOPT_HEADER => true,
        CURLOPT_SSL_VERIFYHOST => false,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_RETURNTRANSFER => true,
        CURLINFO_HEADER_OUT => true,
        CURLOPT_HTTPHEADER => ['user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) 
        AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'],
 ]);
 $response = curl_exec($curl);
 $headers  = curl_getinfo($curl);
 /** обрезаем лишнее из headers **/
 $header_content = substr($response, 0, $headers['header_size']);
 curl_close($curl);
/**
Для нас важен кукис csrftoken</b>, его мы устанавливаем в header x-csrftoken для дальнейшего запроса авторизации. 

Парсим куки:
**/
$cookie = [];
preg_match_all("/Set-Cookie:\s*(?<cookie>[^=]+=[^;]+)/mi", $header_content, $matches);
foreach ($matches['cookie'] as $c) {
            if ($c = str_replace(['sessionid=""', 'target=""'], '', $c)) {
                $c = explode('=', $c);
                $cookie = array_merge($cookie, [trim($c[0]) => trim($c[1])]);
            }
        }
if (isset($cookie['csrftoken']) {
/**
проверяем вернул установил нам инстаграм кукис csrftoken
если нет куки возможно ваш IP или Прокси в черных списках.
**/
}

Дальше я не буду приводить примеры с работой CURL.

Авторизация:

/** отправляем POST запрос на https://www.instagram.com/accounts/login/ajax/ **/
POST
     https://www.instagram.com/accounts/login/ajax/ 
HEADER
     Content-Type: application/x-www-form-urlencoded
    /** id app можно взять из заголовков инстаграм (не стал искать каким хуком он ставится)  он всегда один и тот же **/
     x-ig-app-id: 1217981644879628
     x-csrftoken: /** полученный ранее кукис csrftoken **/
     cookie: /** Все полученные куки ранее **/
     user-agent: /** установленный ранее **/
BODY
     username=/** логин инстаграм **/&password=/** пароль инстаграм **/&queryParams={}&optIntoOneTap=false
 /** Если запрос прошел удачно и мы Авторизовались ответ json, сохраняем куки которые вернул нам сервер и  userId **/
{"authenticated": true, "user": true, "userId": "****", "loginNonce": "****", "reactivated": true, "status": "ok"}
 /** Если неверный логин или пароль ответ json **/
{"authenticated": false, "user": true, "status": "ok"}
 /** В любых других случаях если ответ не в формате json и присутствуют редиректы (вы что то делаете не так) **/

Поиск пользователей


Для этого нам потребуются ранее полученные куки и заголовок x-csrftoken.

/** отправляем POST запрос на https://www.instagram.com/web/search/topsearch/ **/
GET 
    https://www.instagram.com/web/search/topsearch/?context=blended&query={ключ поиска}&rank_token={рандомное число 0.87979}&include_ree=true
HEADER
     x-csrftoken: /** полученный ранее кукис csrftoken **/
     x-ig-app-id: 1217981644879628
     cookie: /** Все полученные куки ранее **/
     user-agent: /** установленный ранее **/
/** Ответ от сервера **/
 {
    "users": [
                 {"position":0, 
                   "user":{
                               "pk":"ид пользователя"
                               "username":"логин в инстаграм"
                               "full_name":"Имя Фамилия, если установлены"
                               ..... и еще куча всего
                               }
                  },{},{}
    ],
    "places":[публикации по месту положения],
    "hashtags":[публикации с хештагеми],
    "has_more": true,
    "rank_token": "0.44093530619864296",
    "clear_client_cache": false,
    "status: "ok"
}

Отправка сообщений в direct


Для отправки сообщений в директ нам потребуются id юзеров кому будем отправлять, как найти пользователей я описал выше.

/** Отправляем POST запрос на api instagram **/
POST
       https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/
HEADER
      /** юзер агент обязательно используем мобильного устройства **/
      user-agent: Instagram 10.2.2 Android (18/4.3; 320dpi; 720x1280; Huawei; HWEVA; EVA-L18; qcom; en_US)
     x-csrftoken: /** полученный ранее кукис csrftoken **/
     x-ig-app-id: 1217981644879628
     cookie: /** Все полученные куки ранее **/
     content-type: application/x-www-form-urlencoded
BODY
     text={текст сообщения}&_uuid=&_csrftoken={полученный ранее кукис csrftoken}&recipient_users="[[ид пользоветелей через запятую]]"&action=send_item&thread_ids=["0"]&client_context={UUID v4 без дефиса}
/** Успешный ответ Ответ от сервера **/
{"status":"ok", "payload":{"item_id":"номер чата"} ...}

/** пример генерации UUID v4 **/
function uuid4()
    {
        if (function_exists('com_create_guid') === true) {
            return trim(com_create_guid(), '{}');
        }
        $data = openssl_random_pseudo_bytes(16);
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
        return vsprintf('%s%s%s%s%s%s%s%s', str_split(bin2hex($data), 4));
    }

Загрузка фотографий через WEB версию


Перед отправкой изображений нужно их сохранить в ImageJPEG качество 100 иначе инстаграм вернет ошибку:

$photo = __DIR__ . '/source.jpg';
$file_temp = __DIR__ . '/send_images.jpg';
list($width, $height, $image_type) = getimagesize(realpath($photo));
$srcImage = ImageCreateFromJPEG($photo);
$resImage = ImageCreateTrueColor($width, $height);
ImageCopyResampled($resImage, $srcImage, 0, 0, 0, 0, $width, $height, $width, $height);
ImageJPEG($srcImage, $file_temp, 100);
ImageDestroy($srcImage);

Загружаем фото:

/** Для загрузки изображения нужно отправить POST запрос **/
/** $microtime получаем время в microtime это будет наш id photo **/
$microtime = round(microtime(true) * 1000);
POST
      https://www.instagram.com/rupload_igphoto/fb_uploader_' . $microtime
HEADER
      content-type: image/jpg
      x-entity-name: 'fb_uploader_' . $microtime /** id photo **/
      offset: 0
      user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X; ru-RU) AppleWebKit/537.36 (KHTML, like Gecko)  Version/11.4.1 Mobile/15G77 Safari/537.36 Puffin/5.2.2IP
      x-entity-length: filesize($file_temp) /** Размер фото **/
      x-instagram-rupload-params: {"media_type":1,"upload_id":"' . $microtime . '","upload_media_height":' . $height . ',"upload_media_width":' . $width . '}
     x-csrftoken: /** полученный ранее кукис csrftoken **/
     x-ig-app-id: 1217981644879628
     cookie: /** Все полученные куки ранее **/
BODY
     /** Без параметров, в body  должно быть только фото **/
     file_get_contents(realpath($file_temp))
/** Успешный ответ от сервера json **/
{"upload_id":"наш ид фото $microtime", "status":"ok" ...}
 
/** На этом еще не все, мы только загрузили фото теперь его надо опубликовать: **/
 
POST
       https://www.instagram.com/create/configure/
HEADER
      content-type: application/x-www-form-urlencoded
      user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X; ru-RU) AppleWebKit/537.36 (KHTML, like Gecko)  Version/11.4.1 Mobile/15G77 Safari/537.36 Puffin/5.2.2IP
      x-csrftoken: /** полученный ранее кукис csrftoken **/
      x-ig-app-id: 1217981644879628
      cookie: /** Все полученные куки ранее **/
BODY
      upload_id=$microtime&caption={текст, хештеги}&usertags=&custom_accessibility_caption=&retry_timeout=
/** Успешный ответ от сервера в json должен содержать */
{"status":"ok", "media":{"id":"***", ...}}


На этом все, я реализовал минимальную обертку над instagram.
Полностью рабочую версию я выложил на github.
Буду рад услышать про реализацию возможно что то я не так сделал.

Похожие публикации

Средняя зарплата в IT

111 111 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 6 788 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 23

    +1
    Сразу бросилось в глаза: Вы используете $srcImage для сохранения картинки в ImageJPEG,
    соответственно следующие строки ненужны:
    list($width, $height, $image_type) = getimagesize(realpath($photo));
    $resImage = ImageCreateTrueColor($width, $height);
    ImageCopyResampled($resImage, $srcImage, 0, 0, 0, 0, $width, $height, $width, $height);

    Но раз уж Вы решили получить $image_type, то думаю стоит его проверить прежде чем вызывать ImageCreateFromJPEG().
      +1
      Спасибо, это dev (beta) накидал так что-бы работало. В дальнейшем учту
        +2
        ненужны
        что-бы

        А были времена:/

      0
      Последний раз, когда я пытался авторизоваться в Инстаграм, там требовался Javascript. Сейчас значит можно и без него.
      А, или тут обязательно должно быть своё app id? Которое просто так не получить.
      Не понятно.
        0
        app_id это версия текущей сборки instagram как я понял, его всегда можно взять в заголовках передаваемых на сервер. Я пока не разбирал откуда он берется. (Скорее всего какая-то статическая либа js его устанавливает)
        +1
        так через раз появляется страница с подтверждение входа (типа новая локация\девайс) — это меня остановило от своей реализации.
        и мама друга говорила, что ИГ может банить такие аккаунты, но я, конечно, это не проверял.
          +3
          Сейчас этот код сложно будет тестировать.

          Что бросилось в глаза:
          • класс InstaLite отвечает за работу с сессиями. По хорошему это должен быть отдельный класс, который содержал бы в себе только те методы, который отвечают за работу с ними.
          • класс Request отвечает и за Response. По хорошему выполнение запроса должно было бы возвращать объект Response, который в свою очередь имел бы необходимые методы для получение информации об ответе от сервера
          • методы all и id класса InstaLite можно вызвать до вызова метода searchUser, что приведет к неверному поведению. По хорошему класс InstaLite не должен содержать этих методов, а вызов метода searchUser должен возвращать новый объект, например FoundUsers, который должен содержать эти методы.


          Хотелось бы вас попросить описать процесс добавления мной, как пользователя вашим пакетом нового API. Например, я решил получить список лайков. Что я должен сделать, чтобы выполнить данный запрос без изменения исходного кода библиотеки.

          P.S.
          Советую как можно скорее перейти к написанию тестов и тогда вы сами начнете дробить свой код на более мелкие части и выделять в отдельные классы. А также почитать про внедрение зависимостей.
            +2

            Боже мой, это хабр?! Единственный здравый комментарий получил минус...

            +1

            Аккуратнее, могут забанить аккаунт. Проверено лично

              0
              А что не так? За что выдают бан?
                +1

                За автоматизацию. Это противоречит правилам использования сервиса.
                Возможно, для бизнес-аккаунтов правила мягче, но мне эта автоматизация стала не актуальна и я больше не изучал этот вопрос.

                  0
                  Позвольте поинтересоваться, какие именно пункты правил противоречат автоматизации?
                  Внимательно перечитал, но кроме пункта A.33 про реверс-инженеринг ничего не нашёл.
                  При этом есть вменяемая официальная документация, например.
                  Если я задумал для себя сделать, например кросс-постинг фотографий своего кота в соцсети, то ничего не нарушается.
                    +1

                    Как минимум, во вменяемой официальной документации сказано:


                    A registered Facebook App with Basic settings configured

                    Вы же используете


                    app_id это версия текущей сборки instagram как я понял, его всегда можно взять в заголовках передаваемых на сервер.

                    Посмотреть заголовки, передаваемые на сервер — это вполне себе реверс-инжиниринг веб-приложения с целью копирования его функционала, что нарушает пункт A 34


                    A 34. Запрещается извлекать исходный код API Instagram или любых приложений Instagram.

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


                    Можете считать меня нудным, но вы, видимо, ещё не сталкивались с модерацией фейсбука.

              0
              Прикольно. Мог бы начать использовать, но нет проекта на php. Вот если бы вы делали как промежуточное звено со своим API (супер, если в openapi/swagger) да еще и в контейнере со всеми php версиями и зависимостями. Тогда использование вашего проекта как proxy-instagram-api очень привлекательно. Плюс безопасность: логин-пароль от инстаграм-аккаунта (в том числе корпоративного) лежит где-то в сервисе, а мои прочие сервисы просто по необходимости и постить могут или комментарии читать.

              Хотя стоп, это уже было github.com/whizzzkid/instagram-proxy-api/issues/28
                0
                Прикольная штука, не смотрел в эту сторону, спасибо!
                0
                Кто-нибудь может подсказать — есть ли статьи по Instagram Basic Display API (по новому API)?
                  0

                  Есть отличное либа который недавно заблокировали: https://mobile.twitter.com/_mgp25/status/1222715332013502464 надеюсь автор выложить куда нибудь ещё можно сшить за новостями.

                    0
                    Я делал библиотеку на Qt для работы с инстаграмом… ну мне прилетело весёлое письмо…
                    0
                    что то Ваш код с гита не работает:
                    PHP Fatal error: Uncaught InstaLite\Exception: Error Authorization in D:\_SCRIPTS_\instalite\vendor\tioffs\instalite\src\InstaLite\InstaLite.php:86
                      0
                      После попытки логина, возникает ошибка
                      'Мы обнаружили необычную попытку входа'
                        0
                        смени x-ig-app-id из своего браузера и все будет ок скорее всего он обновился

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое