Получаем посты из своего аккаунта Instagram посредством нового API Instagram Basic Display

Для многих уже не секрет, что в конце 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 и он позволяет бесплатно хостить простые приложения, но вы можете использовать любой подходящий вариант. 

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

Сперва проверяем дату создания токена. Если токен был создан около 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
Tags:
instagram api, php, curl, tutorial

You can't comment this post because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author's username will be hidden by an alias.