Что делать если Instagram не дал доступ к API?

1 июня 2016 года Instagram отключит от своего API все приложения, которые не прошли модерацию. Что делать если вы в их числе?

Предыстория


Мы делаем сервис для постинга в Instagram по расписанию и используем API для получения информации об аккаунтах. Самим постингом занимаются телефоны в автоматическом режиме. Нам отказали в доступе к API после 1 июня (пробовали пройти модерацию два раза) поэтому было решено найти замену.

Сначала расскажу как мы использовали официальный API:
  1. При добавлении аккаунта забираем из Instagram информацию об аккаунте: имя, фото профайла, количество постов, подписчиков, подписок.
  2. Перед тем как опубликовать фото/видео мы запрашиваем количество постов, и тоже самое после публикации, если число постов увеличилось считаем публикацию успешной.
  3. Если публикация прошла успешно забираем ссылку на последнее фото в профайле.
  4. Если пользователь удаляет фото из нашего сервиса, то перед тем как выполнить задачу нужно проверить существует ли такой пост в Instagram (или его удалили).

Реализация


У Instagram есть веб-версия. С помощью нее в приватных аккаунтах можно получить информацию о количестве постов, подписок и подписчиков, а в публичных еще и сами посты, комментарии и лайки. Поэтому, в силу простоты получения, я подумал, что уже написаны подобные библиотеки. Пошел гуглить и нашел только для NodeJS. И для PHP нашелся какой-то код, но всем четырем пунктам не соответствовал. В итоге было решено писать свою библиотеку.

Не буду вдаваться в детали, так как вы можете посмотреть код на GitHub. Расскажу только ключевые моменты.

Получение информации об аккаунте


Если зайти в профайл (например, instagram.com/kevin) и посмотерть исходный код страницы, то прокрутив вниз можно увидеть зашитый прямо в страницу JSON объект с информацией об аккаунте.
Довольно просто вытаскиваем его (для удобства я использовал mashape/unirest-php), парсим и записываем в массив:

$response = Request::get('https://instagram.com/kevin');
$pageString = $response->body();
$arr = explode('window._sharedData = ', $pageString);
$json = explode(';</script>', $arr[1]);
$userArray = json_decode($json[0], true);
$userData = $userArray['entry_data']['ProfilePage'][0]['user'];
echo $userData['username']; // Теперь можно делать вот так
echo $userData['follows']['count'] // или вот так
echo $userData['is_private']; // ну вы поняли


Получение всех постов в аккаунте


Как оказалось, можно получить готовый JSON последних 20 постов добавив к URL аккаунта /media: https://instagram.com/kevin/media

Но, что делать если нам нужны все посты? Достаточно добавлять в URL параметр max_id с id последнего поста из 20-ки в цикле, пока все посты не кончатся: https://instagram.com/kevin/media?max_id=id. Для удобства даже есть поле more_available, которое принимает значение true или false.

Информация об отдельном посте


Что если у вас есть ссылка на пост в Instagram (например, www.instagram.com/p/9BDXa_L7bm) и вы хотите получить о нем информацию? Тоже самое, что и со страницей профайла, туда вшит JSON с данными о посте.
Тоже как в первом пункте: вытаскиваем, парсим и, бум, у нас есть инфо о посте.

Бонус. Как получить фото из Instagram в лучшем качестве?


Самое лучшее качество фотографии в Instagram на данный момент 1080 пикселей. Но наше решение отдает лишь 640.
Методом тыка мы поняли, что если, например, заменить в URL фото
https://scontent.cdninstagram.com/t51.2885-15/s640x640/sh0.08/e35/12950481_1753078061593396_874826488_n.jpg

часть с 640x640 на 1080x1080:
https://scontent.cdninstagram.com/t51.2885-15/s1080x1080/sh0.08/e35/12950481_1753078061593396_874826488_n.jpg

То получим фото в максимально возможном качестве.

Заключение


В нашем случае с помощью библиотеки удалось полностью перекрыть потребности в API от Instagram.

Репозиторий: github.com/raiym/instagram-php-scraper
Почти тоже самое на Java: github.com/raiym/instagram-java-scraper
Сайт проекта: postaddict.me

Update: Большое спасибо simpel и toly за информацию. На основе ваших комментариев были перписаны запросы к Instagram и добавлены новые.

Update 2 (2 декабря 2016): Развитие библиотеки ушло довольно далеко после публикации данной статьи (добавлены: получение информации о пользователе по id, получение комментариев, пагинация по тегам, получение фото в лучшем доступном качестве, поиск и т.д.), поэтому советуем смотреть репозиторий.

Update 3 (16 марта 2017): Пару дней назад Instagram обновился. Теперь, например, для получения фотографий по тегу требуется авторизация. Сегодня добавили это в библиотеку.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 72

    +1
    За два последних года исходный код страницы в веб-версии Instagram кардинально менялся два раза (меняли названия переменных, содержание), плюс они могут добавлять и удалять пробелы в синтаксисе по своему усмотрению.
    Ну и использовать explode для получения подстроки в строке, это по меньшей мере кощунство. Можно было применить substr+strpos, preg_match, strstr.
      0
      Будем следить за этим, например, в мобильном приложении мы поддерживаем два вида постинга (отличаются расположением кнопок).

      Спасибо за замечание.
      +6
      Информацию о пользователе можно получить сразу в JSON: https://www.instagram.com/kevin/?__a=1

      Тоже самое с отдельным постом: https://www.instagram.com/p/BGBgSw0tpHQ/?__a=1
        0
        О, вот это круто! Надеюсь переживет 1 июня, добавим как альтернативу
          0
          Похоже, что пережило.
            0
            Ага, я уже залил изменения, но до релиза еще надо дописать один метод и все оттестить
              0
              вы не знаете есть способ для нахождения фотографий по местам? lat=...&long=...?
                0
                Да, вот такой https://www.instagram.com/explore/locations/278608830/?__a=1 только нужно прописать в хедер REFERER = https://www.instagram.com и будет JSON, а не пустой ответ
                  0
                  Наверное это не совсем то, что нужно, это данные по location_id
                    0
                    Я вот пробовал подставлять /locations/search/?__a=1&lat=48.858844&lng=2.294351 вариации вот таких вещей, но не сработало)
                    0
                    Спасибо. как понимаю location id из фэйсбука берется. И referer не указывал, вернулся не пустой JSON
            0
            а что по скорости работы в отличии от API?
              0
              Не замеряли даже, нам главное было данные получить
              +1
              Я наверное нехороший буратино, но я бы наверное пошёл по пути разбора официального приложения и дёргания их API с их ключами. Собственно, мало чем отличается, только так есть хотя бы документированный API.
                0
                Тогда вам придется сначала указать логин и пароль, насколько я понимаю.
                Вот, кстати, живой проект по этой теме https://github.com/mgp25/Instagram-API
                +1
                А вы постите действительно реальными устройствами или все же через эмулятор? И, кстати, почему №1 вы для постинга?) Никогда не слышал про вас.
                  0
                  Да, действительно с реальных устройств, вот видео-пруф: https://www.youtube.com/watch?v=CsJKb4RX-jo
                  Эмуляторы не используем, но со счетов их не скидываем.

                  Парень, который занимался PR предложил так написать =D Когда разбирались в теме, мы нашли только одну компанию, которая заявила, что использует настоящие устройства schedugr.am, но подтверждение их слов мы не видели. Остальные сервисы пользуются приватным API.

                  Кажется о нас вообще мало кто слышал, потому что рекламой мы активно не занимаемся пока.
                  0
                  preg_match('/window\._sharedData = (.+);/ui', $content, $matches);

                    0
                    Максимально доступное качество фото можно получить, удалив все кроме /t/ из ссылки — https://scontent.cdninstagram.com/t/12950481_1753078061593396_874826488_n.jpg, через параметры в ссылке можно управлять размером и кропом.

                    или вот так: https://instagram.com/p/9BDXa_L7bm/media/?size=l

                    Лента залогиненного пользователя в JSON: https://www.instagram.com/?__a=1
                    Без сессии ответ будет пустой {}
                        0
                        Зарепортили баг с пагинацией по хештегам

                        В коде библиотеки пагинация сделана через передачу параметра max_id.
                        И сейчас он не работает, то есть первая пачка медиа есть, но следующая возвращается с пустым массивом.
                        На сколько я понял параметр max_id, то работает, то не работает. Не знаете стабильный способ?
                          0
                          вроде с tag.media.page_info.end_cursor в качестве max_id что-то меняется в фиде, пока не разбирался
                            0
                            Действительно -_- Спасибо! Сейчас запилю

                            Вы есть на гитхабе?
                            0
                            С тегами не проверял, но про страницу геолокации (https://www.instagram.com/explore/locations/278324317/) точно могу сказать, что фид меняется и в случае использования start_cursor, в случае использования end_cursor. Причем если смотреть на время картинок, то всегда выдаются более поздние картинки (и при start_cursor, и при end_cursor)
                              0
                              Перепутал — выдаются более ранние картинки
                                0
                                А вы не знаете как получить из user_id username?
                                  0
                                  Знаю только способы с использованием приватного или официального апи
                                    0
                                    Хотя… Если для каждого пользователя хранить какую-либо картинку (идентификатор картинки), то получив информацию о картинке, можно узнать новое имя пользователя
                                      0
                                      Например, можно добавить метом getUserByMediaId. А ссылку на фото/видео получать из mediaId вот таким путем
                                      Но все равно не очень удобно получается.
                                        0
                                        Можно сразу сохранять user.media.nodes[0].code из https://www.instagram.com/username/?__a=1
                                0
                                Странно, но сейчас max_id=mediaId работает опять и в тоже время max_id=end_cursor тоже работает
                              0
                              https://www.instagram.com/explore/tags/test/?__a=1 перестал работать (
                                0
                                Если вы на PHP, то я выпустил обновление
                                  0
                                  да, спасибо
                              0
                              Круто!
                              А как управлять размером и кропом через параметры?

                              Если не секрет, каким способом находятся такие ссылки?

                              Обязательно добавим это в библиотеку.
                                0
                                у меня популярное приложение на instagram api и я озадачился этим вопросом достаточно давно. нашел перебором параметров и изучением скриптов.
                                управление кропом описано тут
                                  0
                                  Ясно, спасибо
                                    0
                                    Из вашего опыта, часто ли менялись параметры и ссылки?
                                      0
                                      за год — не менялось, скорее всего эти ендпоинты активно используются различными сервисами-партнерами и менять их достаточно сложно
                                        0
                                        очень удобно
                                0
                                Вот gem для Ruby на основе информации из этого поста и комментариев:
                                https://github.com/sborod/ruby-instagram-scraper
                                  0
                                  Это хороший конечно способ, но может быть у нас уже появился рынок верифицированных клиентов, или хотя-бы access_token?
                                    0
                                    Интересно. Я о таком не знаю.

                                    Плюс зависеть от чужого токена не оч приятно. Кстати, как-то я искал на github рабочие клиенты/токены как-то давно и находил рабочие
                                    0
                                    Передо мной тоже была цель вытянуть несколько фотографий, так как с начала месяца старые скрипты рухнули. И казалось бы https://www.instagram.com/username/media/ прекрасно отдает данные json. Но ведь ajax'ом нельзя вытянуть данные на моем домене с домена инстаграма. Ок, берем dataType: jsonP. Не работает. Все же результат json <> jsonp
                                    //JSON
                                    {"name":"stackoverflow","id":5}
                                    //JSONP
                                    func({"name":"stackoverflow","id":5});
                                    

                                    Нагуглил решение прогонять запрос через сторонний сервис: http://www.whateverorigin.org/
                                    Получилось типа того: http://jsfiddle.net/VpGVL/
                                      0
                                      Кто-нибудь нашел как получить все комментарии к посту, а не только 40 последних?

                                        0
                                        пока вообще не пробовал работать с комментариями
                                          0
                                          Получилось получить все комментарии из любого media, в последнем релизе библиотеки теперь доступно: https://github.com/raiym/instagram-php-scraper
                                          Тестил на 8 000 комменатриев.

                                          Вот этими двумя url можно сделать пагинацию:

                                          https://www.instagram.com/query/?q=ig_shortcode(BG3Iz-No1IZ){comments.last(300){count,nodes{id,created_at,text,user{id,profile_pic_url,username,follows{count},followed_by{count},biography,full_name,media{count},is_private,external_url,is_verified}},page_info}}

                                          https://www.instagram.com/query/?q=ig_shortcode(BG3Iz-No1IZ){comments.before(17856915757025183,300){count,nodes{id,created_at,text,user{id,profile_pic_url,username,follows{count},followed_by{count},biography,full_name,media{count},is_private,external_url,is_verified}},page_info}}
                                            0
                                            Круто, спасибо. Буду на выходных смотреть.
                                              0
                                              Как можно получить все фотографии из любого media?
                                                0
                                                Что вы имеете ввиду под медиа?
                                                Media это фото или видео
                                            0
                                            Есть ли способ получить username по user id?
                                              0
                                              Нет, но хотелось бы)
                                                0
                                                Закомитил сегодня эту возможность. Вот так можно получить

                                                https://www.instagram.com/query/?q=ig_user(3){id,username,external_url,full_name,profile_pic_url,biography,followed_by{count},follows{count},media{count},is_private,is_verified}
                                                  0
                                                  Круто! Спасибо!
                                                    0
                                                    Спасибо raiym!
                                                  0
                                                  Может кто-нибудь знает как достать все лайки у фото/видео?
                                                    0
                                                    https://www.instagram.com/p/%post_is%/?__a=1
                                                    {media{likes{nodes}}}
                                                    Не оно?
                                                      0
                                                      А как сделать, например, пагинацию? Чтобы получить все лайки. Насколько я понял нужно залогиниться, без этого нельзя.
                                                        0
                                                        Логиниться не обязательно, но как сделать пагинацию меня тоже интересует, потому что показывает только 10 лайкнувших.
                                                          0
                                                          А вы не поняли как получить все лайки?
                                                    0
                                                    Как получить все ссылки на изображения по хештегу? Библиотека умеет такое?
                                                    И с авторизацией больше данных отдаётся через параметр "?__a=1". Удалось как-нибудь прикрутить авторизацию?
                                                      0
                                                      Получать данные по хештегу Instagram::getMediasByTag('zara', 30);

                                                      По поводу авторизации — ее не будет (в ближайшем будущем). проект для получения только публичных данных

                                                      вот есть другой: https://github.com/mgp25/Instagram-API
                                                        0
                                                        Авторизацию пришлось добавить, так как инстаграм требует ее для получения фоток по тегу и локации
                                                      0
                                                      Не нашел не в исходниках, не в описании, как получить список подписчиков. Я вижу кол-во, но мне нужен конкретный список.
                                                      Альтернатива метода
                                                      /self/follows
                                                        0
                                                        В библиотеке пока нет такой возможности, но технически сделать можно.
                                                          0
                                                          Можете порадовать?)
                                                            0
                                                            Да, буду рад, видеть вас в котрибьюторах)
                                                            А если серьезно, то пока такую функциональность никто не имплементировал
                                                              0
                                                              Ну будьте первым) Как вас замативировать)
                                                        0
                                                        Ну вот параметр '?__a=1' отключили… что будете делать?

                                                        Only users with full accounts can post comments. Log in, please.