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.
Поделиться с друзьями
-->

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


  1. CnapoB
    30.05.2016 12:32
    +1

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


    1. raiym
      30.05.2016 13:47

      Будем следить за этим, например, в мобильном приложении мы поддерживаем два вида постинга (отличаются расположением кнопок).

      Спасибо за замечание.


  1. toly
    30.05.2016 12:35
    +6

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

    Тоже самое с отдельным постом: https://www.instagram.com/p/BGBgSw0tpHQ/?__a=1


    1. raiym
      30.05.2016 13:46

      О, вот это круто! Надеюсь переживет 1 июня, добавим как альтернативу


      1. simpel
        02.06.2016 16:10

        Похоже, что пережило.


        1. raiym
          02.06.2016 16:13

          Ага, я уже залил изменения, но до релиза еще надо дописать один метод и все оттестить


        1. raiym
          02.06.2016 16:14

          вы не знаете есть способ для нахождения фотографий по местам? lat=...&long=...?


          1. simpel
            02.06.2016 16:25

            Да, вот такой https://www.instagram.com/explore/locations/278608830/?__a=1 только нужно прописать в хедер REFERER = https://www.instagram.com и будет JSON, а не пустой ответ


            1. simpel
              02.06.2016 16:28

              Наверное это не совсем то, что нужно, это данные по location_id


              1. raiym
                02.06.2016 16:33

                Я вот пробовал подставлять /locations/search/?__a=1&lat=48.858844&lng=2.294351 вариации вот таких вещей, но не сработало)


            1. raiym
              02.06.2016 16:30

              Спасибо. как понимаю location id из фэйсбука берется. И referer не указывал, вернулся не пустой JSON


  1. Protos
    30.05.2016 15:26

    а что по скорости работы в отличии от API?


    1. raiym
      30.05.2016 16:16

      Не замеряли даже, нам главное было данные получить


  1. Evengard
    30.05.2016 16:10
    +1

    Я наверное нехороший буратино, но я бы наверное пошёл по пути разбора официального приложения и дёргания их API с их ключами. Собственно, мало чем отличается, только так есть хотя бы документированный API.


    1. raiym
      30.05.2016 16:14

      Тогда вам придется сначала указать логин и пароль, насколько я понимаю.
      Вот, кстати, живой проект по этой теме https://github.com/mgp25/Instagram-API


  1. sphinks
    30.05.2016 16:23
    +1

    А вы постите действительно реальными устройствами или все же через эмулятор? И, кстати, почему №1 вы для постинга?) Никогда не слышал про вас.


    1. raiym
      30.05.2016 16:32

      Да, действительно с реальных устройств, вот видео-пруф: https://www.youtube.com/watch?v=CsJKb4RX-jo
      Эмуляторы не используем, но со счетов их не скидываем.

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

      Кажется о нас вообще мало кто слышал, потому что рекламой мы активно не занимаемся пока.


  1. Dim4sik
    30.05.2016 17:11

    preg_match('/window\._sharedData = (.+);/ui', $content, $matches);


  1. simpel
    30.05.2016 22:59

    Максимально доступное качество фото можно получить, удалив все кроме /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
    Без сессии ответ будет пустой {}


    1. simpel
      30.05.2016 23:08

      1. raiym
        06.06.2016 23:25

        Зарепортили баг с пагинацией по хештегам

        В коде библиотеки пагинация сделана через передачу параметра max_id.
        И сейчас он не работает, то есть первая пачка медиа есть, но следующая возвращается с пустым массивом.
        На сколько я понял параметр max_id, то работает, то не работает. Не знаете стабильный способ?


        1. simpel
          06.06.2016 23:50

          вроде с tag.media.page_info.end_cursor в качестве max_id что-то меняется в фиде, пока не разбирался


          1. raiym
            07.06.2016 00:10

            Действительно -_- Спасибо! Сейчас запилю

            Вы есть на гитхабе?


            1. simpel
              07.06.2016 00:37

          1. toly
            07.06.2016 11:13

            С тегами не проверял, но про страницу геолокации (https://www.instagram.com/explore/locations/278324317/) точно могу сказать, что фид меняется и в случае использования start_cursor, в случае использования end_cursor. Причем если смотреть на время картинок, то всегда выдаются более поздние картинки (и при start_cursor, и при end_cursor)


            1. toly
              07.06.2016 11:22

              Перепутал — выдаются более ранние картинки


              1. raiym
                07.06.2016 11:27

                А вы не знаете как получить из user_id username?


                1. toly
                  07.06.2016 13:59

                  Знаю только способы с использованием приватного или официального апи


                1. toly
                  07.06.2016 15:03

                  Хотя… Если для каждого пользователя хранить какую-либо картинку (идентификатор картинки), то получив информацию о картинке, можно узнать новое имя пользователя


                  1. raiym
                    07.06.2016 15:09

                    Например, можно добавить метом getUserByMediaId. А ссылку на фото/видео получать из mediaId вот таким путем
                    Но все равно не очень удобно получается.


                    1. toly
                      07.06.2016 15:17

                      Можно сразу сохранять user.media.nodes[0].code из https://www.instagram.com/username/?__a=1


    1. raiym
      30.05.2016 23:30

      Круто!
      А как управлять размером и кропом через параметры?

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

      Обязательно добавим это в библиотеку.


      1. simpel
        30.05.2016 23:34

        у меня популярное приложение на instagram api и я озадачился этим вопросом достаточно давно. нашел перебором параметров и изучением скриптов.
        управление кропом описано тут


        1. raiym
          30.05.2016 23:36

          Ясно, спасибо


        1. raiym
          30.05.2016 23:37

          Из вашего опыта, часто ли менялись параметры и ссылки?


          1. simpel
            30.05.2016 23:38

            за год — не менялось, скорее всего эти ендпоинты активно используются различными сервисами-партнерами и менять их достаточно сложно


            1. raiym
              30.05.2016 23:40

              очень удобно


  1. sborod
    01.06.2016 10:53

    Вот gem для Ruby на основе информации из этого поста и комментариев:
    https://github.com/sborod/ruby-instagram-scraper


  1. MorozovNsk
    03.06.2016 01:16

    Это хороший конечно способ, но может быть у нас уже появился рынок верифицированных клиентов, или хотя-бы access_token?


    1. raiym
      03.06.2016 08:45

      Интересно. Я о таком не знаю.

      Плюс зависеть от чужого токена не оч приятно. Кстати, как-то я искал на github рабочие клиенты/токены как-то давно и находил рабочие


  1. Sepaka
    07.06.2016 11:19

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