Для многих уже не секрет, что в конце 2019 года Instagram API изменилось. При этом старое API все еще поддерживается — отключение изначально планировалось в начале весны 2020, но позже было отложено на 29 июня 2020. После этой даты все приложения, использующие устаревшее API, перестанут работать. В этом небольшом туториале мы разберем как можно вывести на странице ленту из своего аккаунта с учетом текущих изменений. Материал рассчитан на новичков в подобного рода интеграциях.


Создание приложения


Для начала нам необходимо создать учетную запись разработчика на Facebook и создать приложение.  Для этого переходим на https://developers.facebook.com/, авторизуемся (если обычного аккаунта Facebook у вас нет, то система в любом случае предложит начать с его создания) и в меню «Мои приложения» выбираем «Создать приложение». После чего указываем название приложения и почту, нажимаем «Создайте ID приложения».





Пройдя капчу, вы попадете в панель управления приложением. Переходим в меню «Настройки» -> «Основное», нажимаем «Добавить платформу» и выбираем «Веб-сайт».



Укажите адрес вашего сайта, на котором вы будете размещать ленту из Instagram. Сохраните изменения. 



Платформу можно не указывать, работать будет и без нее. Но в официальной документации данный пункт присутствует, поэтому я включил его в туториал. Скорее всего это необходимо для прохождения приложением проверки, но мы обойдемся и без нее. Так как нам достаточно только создать токен для доступа к данным аккаунта — об этом далее.

Затем перейдите в меню «Товары» и выберите Instagram, нажав на кнопку «Настроить». Страница обновится и появится дополнительное меню слева. Перейдите в меню «Basic Display» и внизу нажмите на кнопку «Create New App».





В открывшемся окне вводим название нашего приложения. После этого откроется страница настроек, где необходимо указать адрес нашего сайта. Во все три поля вводим одно и то же значение — этого будет достаточно для нашей задачи. Обратите внимание, что адрес сайта обязательно должен быть с https. Сохраняем данные и далее нажимаем на кнопку «Add or Remove Instagram Testers».





Внизу страницы нажимаем на «Добавить Instagram Testers». В окошке вбиваем название аккаунта, из которого будем тянуть фотографии и нажимаем «Отправить».



Далее нам необходимо перейти на сайт https://www.instagram.com/, залогиниться в аккаунт, указанный в приложении как тестировщик, и перейти в настройки, выбрав пункт «Apps and Websites».



Переходим во вкладку «Tester invites» и нажимаем «Accept» напротив нашего приложения.



Возвращаемся в кабинет разработчика Facebook и переходим в меню Basic Display и напротив нашего аккаунта нажимаем «Generate Token».



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



Это и есть так называемый long-lived token, который будет действителен в течении 60 дней. И именно его можно периодически обновлять — обновление доступно спустя сутки после создания/обновления предыдущего токена.

Получение ленты Instagram на PHP


Итак, настроив приложение в кабинете разработчика Facebook, можно приступать к интеграции ленты на сайт. Я буду использовать Heroku, так как на нем есть https и он позволяет бесплатно хостить простые приложения, но вы можете использовать любой подходящий вариант. Также обратите внимание на то, что в примерах кода используется библиотека curl — убедитесь, что она у вас установлена.

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

Сперва проверяем дату создания токена. Если токен был создан около 60 дней назад (но не более, иначе обновить не получится), то сперва нам нужно его обновить. Для этого используем endpoint GET /refresh_access_token. 

$url = "https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=" . $accessToken;

$instagramCnct = curl_init(); // инициализация cURL подключения
curl_setopt($instagramCnct, CURLOPT_URL, $url); // адрес запроса
curl_setopt($instagramCnct, CURLOPT_RETURNTRANSFER, 1); // просим вернуть результат
$response = json_decode(curl_exec($instagramCnct)); // получаем и декодируем данные из JSON
curl_close($instagramCnct); // закрываем соединение

// обновляем токен и дату его создания в базе

$accessToken = $response->access_token; // обновленный токен

Данные приходят в JSON-формате, а сам токен содержится в свойстве access_token.

Обновив токен, или же если он у нас был не такой уж и старый, мы можем отправить запрос на получение ленты из нашего аккаунта. С помощью Basic Display API можно получить изображения, видео, а также подписи к ним и ссылки на публикации. Для получения данных нам понадобится endpoint GET /me/media

$url = "https://graph.instagram.com/me/media?fields=id,media_type,media_url,caption,timestamp,thumbnail_url,permalink&access_token=" . $accessToken;
$instagramCnct = curl_init(); // инициализация cURL подключения
curl_setopt($instagramCnct, CURLOPT_URL, $url); // адрес запроса
curl_setopt($instagramCnct, CURLOPT_RETURNTRANSFER, 1); // просим вернуть результат
$media = json_decode(curl_exec($instagramCnct)); // получаем и декодируем данные из JSON
curl_close($instagramCnct); // закрываем соединение

Данные будут содержаться в свойстве data. Список всех возвращаемых свойств:



Но тут возникает проблема, решение которой я не нашел в документации. Если в ленте у нас есть публикации в виде альбомов (media_type будет равен CAROUSEL_ALBUM), то есть несколько изображений/видео в одном посте, то в ответе на запрос придет только первое изображение в карусели. Для получения всех изображений в параметре fields нам нужно указать параметр children. Но и в этом случае мы получим только  id изображений:

stdClass Object
(
    [data] => Array
        (
            [0] => stdClass Object
                (
                    [id] => 18140723445053387
                    [media_type] => CAROUSEL_ALBUM
                    [media_url] => https://scontent-iad3-1.cdninstagram.com/v/...
                    [caption] => caption
                    [timestamp] => 2020-04-22T11:19:28+0000
                    [permalink] => https://www.instagram.com/p/B_R_5I.../
                    [children] => stdClass Object
                        (
                            [data] => Array
                                (
                                    [0] => stdClass Object
                                        (
                                            [id] => 17880333085571127
                                        )
                                    [1] => stdClass Object
                                        (

                                            [id] => 17895333339472851
                                        )
                                    [2] => stdClass Object
                                        (
                                            [id] => 18107333661126811
                                        )
                                )
                        )
                )
        )
)

В документации указано, что для получения изображений из карусели необходимо использовать endpoint GET /{media-id}/children. Но выходит что на каждую карусель нам необходимо посылать дополнительный запрос к API. А если мы получаем 50 каруселей? Я решил поэкспериментировать и в итоге нашел способ получения данных изображений карусели в изначальном запросе. Для этого я использовал следующий формат:

$url = "https://graph.instagram.com/me/media?fields=id,media_type,media_url,caption,timestamp,thumbnail_url,permalink,children{fields=id,media_url,thumbnail_url,permalink}&limit=50&access_token=" . $accessToken;

Отправив такой запрос, мы получим все изображения постов, а в свойстве children помимо id будут находится все свойства, перечисленные в параметре запроса children{fields=...}.  

В итоге, наш окончательный код будет выглядеть так:

$accessToken = "token"; // получаем токен из базы
$tokenDate = "date_from"; // получаем дату создания из базы

// Вычисляем сколько полных дней прошло с даты создания токена
$tokenTimestamp = strtotime($tokenDate);
$curTimestamp = time();
$dayDiff = ($curTimestamp - $tokenTimestamp) / 86400;

if (!empty($accessToken)) {
  if ($dayDiff > 50) { // Если токену уже более 50 дней, то обновляем его

    // Запрос на обновление токена
    $url = "https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=" . $accessToken;
    $instagramCnct = curl_init(); // инициализация cURL подключения
    curl_setopt($instagramCnct, CURLOPT_URL, $url); // адрес запроса
    curl_setopt($instagramCnct, CURLOPT_RETURNTRANSFER, 1); // просим вернуть результат
    $response = json_decode(curl_exec($instagramCnct)); // получаем и декодируем данные из JSON
    curl_close($instagramCnct); // закрываем соединение

    // обновляем токен и дату его создания в базе

    $accessToken = $response->access_token; // обновленный токен
  }

  // Получаем ленту
  $url = "https://graph.instagram.com/me/media?fields=id,media_type,media_url,caption,timestamp,thumbnail_url,permalink,children{fields=id,media_url,thumbnail_url,permalink}&limit=50&access_token=" . $accessToken;
  $instagramCnct = curl_init(); // инициализация cURL подключения
  curl_setopt($instagramCnct, CURLOPT_URL, $url); // подключаемся
  curl_setopt($instagramCnct, CURLOPT_RETURNTRANSFER, 1); // просим вернуть результат
  $media = json_decode(curl_exec($instagramCnct)); // получаем и декодируем данные из JSON
  curl_close($instagramCnct); // закрываем соединение

  $instaFeed = array();
  foreach ($media->data as $mediaObj) {
    if (!empty($mediaObj->children->data)) {
      foreach ($mediaObj->children->data as $children) {
        $instaFeed[$children->id]['img'] = $children->thumbnail_url ?: $children->media_url;
        $instaFeed[$children->id]['link'] = $children->permalink;
      }
    } else {
      $instaFeed[$mediaObj->id]['img'] = $mediaObj->thumbnail_url ?: $mediaObj->media_url;
      $instaFeed[$mediaObj->id]['link'] = $mediaObj->permalink;
    }
  }
}

На выходе я получаю переменную $instaFeed, содержащую массив с превью изображений/видео и ссылками на эти публикации в Instagram.

Обратите внимание, что я использую дополнительный параметр limit для указания количества получаемых постов. Если его не указать, то вернется количество по умолчанию, а именно 25. Если постов больше, чем запрашиваемое количество, то в ответе придет свойство paging, где будут указаны ссылки для пагинации, то есть для получения следующих и предыдущих постов.

Работающий код можно посмотреть тут.
Исходники тут.

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

Используемые материалы:
Документация Basic Display API

Комментарии (8)


  1. Survtur
    20.09.2021 20:10
    +2

    Еще вариант для получения своих записей: запросить и выкачать архив со своим контентом и уже потом распарсить его на локальной машине


    1. spcazaam Автор
      20.09.2021 20:37

      Правильно ли я понял, что при каждом обновлении контента необходимо будет заново выкачивать архив? Если да, то статья как раз о том, как один раз написать скрипт и получать актуальные посты из своего аккаунта в режиме реального времени.


      1. Survtur
        21.09.2021 00:25
        +1

        Ну да, архив он для разовой нужды. Зато лимит на запросы случайно не получиться превысить :)


  1. Fasterpast
    20.09.2021 23:25

    Вообще, удивительно, что блок с постами собственной Инсты на собственном же сайте - такая нетривиальная задача... Мне пришлось еще и "кэш" делать, иначе количество запросов в час легко превысить.


    1. spcazaam Автор
      02.11.2021 02:09

      Да, конечно, для посещаемого ресурса кеш - отличное решение.


  1. Jack_Rabbit
    22.09.2021 07:25

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

    В итоге я перешел на использование парсеров вроде https://github.com/postaddictme/instagram-php-scraper вместе с локальным кешем изображений. Решение так себе, но зато просто подключить и не нужно объяснять клиенту как получить какой-то токен.


  1. Kamil_Minikeev
    02.11.2021 02:10

    А для чего нужно обновлять api токен? Его нельзя просто получить 1 раз и все?


    1. spcazaam Автор
      02.11.2021 02:11

      Нет, токен живет определенное время, после чего его необходимо обновлять. Именно поэтому в примерах кода есть проверка на "возраст" токена.