
Ни для кого не секрет, что самая популярная и прибыльная площадка для рекламы, бизнеса и прочего — Instagram. Почему им стал именно сервис, в котором по началу можно было загружать только картинки определённого размера (соотношение сторон имеется ввиду) и не было абсолютно ничего, что было в тогдашних соцсетях — совсем непонятно, но факт есть факт. Ввиду чего все стараются проникнуть на площадку Instagram и захватить оттуда наибольшее количество аудитории, и делают, это, конечно же, не вручную. А за этим следует, что Instagram жёстко блокирует доступ для ботов, спамеров и прочему, дабы сеть оставалась чистой.
- Самые полезные функции (постинг и удаление постов) доступны только из мобильного приложения Instagram, эмуляция запросов сложна, так как надо вытащить из приложения ключ, который с каждой новой версией обновляется.
- Web-версия обрезана, но радует, что в ней есть возможность лайкать, комментировать и удалять комментарии
- Есть API, но процедура его получения удручающе долгая и спамерам и ботам такой путь точно не светит. Плюс было много моментов, когда соглашения в API менялись, что не всегда удобно.
Хоть я и связался с Instagram не для того, чтобы сделать очередного спам-бота, который может подписываться и лайкать, но возиться с получением Instagram API мне ну очень не хотелось, поэтому пришлось написать собственную библиотеку для взаимодействия с Instagram.
Хочется сказать, что работать с Web-версией Instagram очень даже приятно по двум причинам:
1. О любой страничке можно получить краткую информацию, если отправить GET запрос вида:
https://instagram.com/zuck/?__a=1
И ответом является JSON с доступной информацией, первыми 10 постами странички и прочим. Очень приятно.
2. Если же краткой информации не хватает, то тут есть ещё одна приятная новость. Дозагрузить фоточки, подписки, комментарии можно со помощью определённого запроса вида
https://www.instagram.com/graphql/query/?query_id=17888483320059182&variables=...
, где в variables передаются переменные для обработки в формате JSON. Ответом также является JSON. Да и вообще, очевидно, что работает это всё на GraphQL, так что чтобы понять, как обрабатываются запросы, можно даже погуглить.
На основе данных знаний и построена вся библиотека. Я вкратце опишу, как её можно использовать, может, кому-то пригодится. Кстати, я там в репозитории указал лицензию BSD 3. Подскажите, может, мне стоит её поменять, чтобы не было никаких трудностей?
Установка
Устанавливать её не надо. Точнее, мне было лень прописывать всякие setup.py или упаковывать, когда библиотека состоит всего из одного файла. Поэтому там просто файл instagram.py, который подключается так:
import instagram
Как ей пользоваться?
Взаимодействие с Instagram возможно либо с авторизацией, либо без. Без авторизации отсутствуют функции просмотра подписок и подписчиков, и, очевидно, невозможно что-то лайкнуть, что-то откомментировать и так далее. Единственные ограничения с авторизацией: это невозможно выкладывать посты и удалять их.
Приведу пример взаимодействия без авторизации:
import instagram
agent=instagram.Agent()
account=instagram.Account("zuck")
agent.update(account)
media=agent.getMedia(account, count=100)
for m in media:
print(m)
Как вы поняли, данный скрипт прогружает информацию о странице Марка Цукерберга, загружает последние 100 постов с его странице и выводит их на экран.
Хочу сказать, что если бы я не написал
agent.update(account)
то выполнить загрузку постов не получилось бы, так как никакой информации о странице Цукерберга известно не было.
А вот пример с авторизацией:
import instagram
agent=instagram.AgentAccount("oleg.yurchik", "imasuperpassword")
agent.update()
account=instagram.Account("zuck")
agent.update(account)
# and etc.
Это, так называемый Hello, world!. Или быстрый старт.
А теперь расскажу подробнее:
Instagram, на самом деле, имеет всего 5 сущностей:
- Аккаунт
- Пост
- Геолокация
- Комментарий
- Хэштег
Всё остальное — это просто списки этих сущностей, такие как лайки, подписки, подписчики и прочее. И для каждой сущности есть свой класс. Для аккаунтов — Account, постов — Media, геолокаций — Location, комментариев — Comment, хэштегов — Tag. И каждый из них (кроме комментариев) нужно обновить, прежде чем работать с ним. То есть если хочется загрузить все свои посты, пролайкать их и получить список геолокаций, то нужно выполнить следующее:
import instagram
agent=instagram.AgentAccount('oleg.yurchik', 'anothersuperpassword')
agent.update()
media=agent.getMedia(count=agent.media_count)
locations=[]
for m in media:
agent.like(m)
agent.update(m)
if agent.location:
locations.append(agent.location)
А если позже надо получить последние 10 постов по определённой геолокации, то надо будет сделать следующее:
agent.update(location)
media=agent.getMedia(location, count=10)
Пришлось вынести функцию обновления аккаунта из инициализации, так как при необходимости получить всех подписчиков, например, программа бы обновляла каждый из аккаунтов, а это нехорошо.
Библиотека основана на библиотеке requests, и одной из фишек я считаю то, что в методы также можно передать дополнительные параметры для requests. Такая идея пришла ко мне, когда я первый раз получил 429 ошибку от Instagram. Нужно было использовать прокси.
Например, можно сделать так:
media=agent.getMedia(count=agent.media_count, settings={'proxies': {'https': '127.0.0.1:80'}})
где 127.0.0.1:80 — можно указать свой прокси
Также ещё одной фишкой, я думаю, может являться перехват ошибок.
В классах Agent и AgentAccount (те, что и производят общение с Instagram) есть словарь, организованный как дерево, он называется exception_actions. В нём в виде ключей хранятся классы исключений, а в виде значений — функции. Если вдруг произошла какая-то ошибка, она перехватывается и выполняется функция из словаря. Этой функции передаётся объект исключения и параметры, с которыми выполнялся запрос. Она может выполнить какое-то действие и вернуть изменённые (или нет) параметры запроса. Выполнение запроса повторится снова. И будет повторятся столько раз, сколько указано в параметре Agent.repeats. По умолчанию стоит 1.
А ещё можно не беспокоиться о переполнении памяти.
У класса каждой сущности есть словарь, в котором хранятся все объекты данного класса (или объекты подкласса даже). Таким образом, если Вы случайно создадите, например, аккаунт, который уже был создан, конструктор вернёт вам ссылку на ранее созданный аккаунт.
Если вы случайно пропустили ссылку на репозиторий в тексте, то вот она ещё раз.
И напоследок скажу, что из-за некоторых решений появились и некоторые проблемы:
- Например, проблема при повторном создании объекта. Если вдруг вы захотите использовать аккаунт как рабочий и взаимодействовать через него, а он уже ранее был создан как обычный аккаунт, то создать его снова не получиться. Пока я не знаю, как это решить.
- Перехват ошибок иногда ведёт себя очень странно и не до конца протестирован.
Я очень надеюсь, что, может быть, данное решение кому-нибудь пригодится, надеюсь на любые полезные комментарии и на помощь в допиливании этой штуки. В указанной мною статье был приведён пример такого скрипта на PHP, но он занимался только сбором информации и, по-моему, он работал только со старой версией веб-нитерфейса Instagram.
Спасибо за внимание.
Комментарии (36)
Hugiron
09.10.2017 00:30+1Очень жаль, что не все методы работают (например, получение лайков и комментариев без авторизации). Насколько мне известно, query_id привязывается к сессии, а вы их вынесли в качестве магических констант.
Ryder95 Автор
09.10.2017 00:34Насколько мне известно, query_id привязывается к сессии, а вы их вынесли в качестве магических констант.
Это действительно магические константы) Точнее, они требуются для того, чтобы указать, что запрашивается. Это проверено опытным путём
Очень жаль, что не все методы работают (например, получение лайков и комментариев без авторизации).
Нет, без авторизации точно можно получать комментарии, а лайки, если честно, не пробовал. Но если web-интерфейс позволяет, то и в приложении можно.
Hazrat
09.10.2017 01:39+1Если кому интересно, делал как-то телегам бот для масс фолловинга t.me/instanode_bot
Использовал приватное api instagram, в итоге имея базу прогоняю по ним аккаунты
Проект на github github.com/hazratgs/instalator-telegram
xilix
09.10.2017 02:21+32 года назад написал плагин, который публикует товары в инстаграм
www.webasyst.ru/store/plugin/shop/instagram
Он эмулирует телефон.
Как работал ключ, так и работает до сих пор.
KoToSveen
09.10.2017 10:09-1когда библиотека состоит всего из одного файла
всего из 756 строк.
Может я и придираюсь, но я бы всё раскидал по разный файлам (эксепшионы, авторизация, действия)Ryder95 Автор
09.10.2017 10:11+2Не, всё верно, но пока я не увидел в этом смысла) и мне удобнее было писать, когда всё в одном файле
firk
09.10.2017 12:00+2756 строк — это не много
А вот делать много файлов с каким-то специальным библиотечным оформлением, когда можно сделать один, встраиваемый под видом части своего исходника — уже наоборот "много".
largotek
09.10.2017 10:11А у инстаграма есть ограничения по запросам? И как это обходите? Например у вк 3 запроса в секунду, и нормально парсить только если сбор распределять на разные аккаунты.
Ryder95 Автор
09.10.2017 10:13Как я выше писал, все запросы делаются на основе библиотеки requests. Если использовать парсинг с авторизацией, то прокси не поможет, это да. Мне приходилось вставлять обработчик ошибок, чтобы при 429 он ожидал какое-то время
largotek
09.10.2017 10:16А сам инстаграм не даёт никаких цифр запросы в секунду? И по опыту можете, пожалуйста, сказать сколько нужно времени (вместе с ожиданием) что бы скачать например 10000 постов?
Ryder95 Автор
09.10.2017 10:33Официально у них описано только API, и там тоже не ахти. В варианте веб версии некоторую инфу можно вытаскивать анонимно, а с API это невозможно. Если про реальные цифры, то чтобы не уйти в 429 я пробовал ставить ожидание после каждого запроса от 1 до 2 секунд. С 1 секундой он уходит в 429 ошибку, с 2 — никогда. Думаю, при таком варианте — это где-то 40-50 запросов в минуту. Так как максимум можно вытащить около 1000 запросов за раз (скорее всего, есть цифра точная, но я не ставил экспериментов пока), то на 10000 постов надо будет потратить около 3-4 часов. Это если быть авторизованным. А если вытаскивать посты без авторизации с использованием прокси — то ограничений нет. Опять же, если бы мобильное приложение или API давали лучшие результаты(
Ryder95 Автор
09.10.2017 10:37Простите, я считаю, как обезьяна. 10000 постов можно вытащить максимум за полминуты, а вот получить картинку, текст и так далее каждого поста — это долго, да
largotek
09.10.2017 11:15Спасибо. Да я имел ввиду картинка, текст, теги и т.д.
Ryder95 Автор
09.10.2017 11:27На самом деле, технически можно достать и за полминуты. По-моему, из запросов типа ?__a=1 можно достать ещё маленькие изображения и, кажется, текст. Но это не точно
largotek
09.10.2017 12:05Хм… интересно, спасибо. Как перейду к инсте надо будет видимо исследовать этот вопрос.
petriichuk
09.10.2017 11:22вот кстати интересный скрипт для накрутки, github.com/timgrossmann/InstaPy слелан с использованиям Selenium
vlade11115
09.10.2017 16:23Для класса который знает о своих экземплярах хорошо бы взять weakref.WeakSet. Тогда не нужно будет писать кастомный __del__, и когда на экземпляр не будет ссылок тогда из WeakSet он пропадёт автоматически. Да и в целом код станет немного проще.
zhuharev
09.10.2017 16:40Ни для кого не секрет, что самая популярная и прибыльная площадка для рекламы, бизнеса и прочего — Instagram.
Сильное заявление
Рекомендую библиотеку, которая эмулирует поведение android-приложения и позволяет делать почти всё (сейчас живые трансляции не доступны), что возможно в приложении https://github.com/mgp25/Instagram-API/.
Если вам нужен только постинг, используйте мой докер-контейнер (https://github.com/zhuharev/instagram-rest-api), который запускается как микро-сервис и позволяет (пока только) постить фото и галлереи просто rest-запросом.inoyakaigor
09.10.2017 18:21Может кто подскажет такое же, но только для ноды?
zhuharev
09.10.2017 19:55Вообще все проекты гуглятся по запросу «instagram unofficial api»
Есть адаптация для ноды https://github.com/huttarichard/instagram-private-api и для питона https://github.com/danleyb2/Instagram-API
Лучше использовать оригинальную библиотеку, потому что у неё бОльшее комьюнити и библиотеку часто обновляют.
kAIST
09.10.2017 18:47+2По поводу эмуляции клиента и эавторизации добавлю. Когда ты делаешь ее на сервере, который географически удален от того места, где ты обычно используешь аккаунт, авторизация не проходит. Нужно зайти в веб версию инстаграма, залогинится там и подтвердить что это был ты.
Столкнулся с этим, когда делал инстаграм клиент для часов pebble (исходники, кому интересно)Ryder95 Автор
09.10.2017 18:50Да, есть такое. Это тоже можно обойти, но из-за того, что эта проблема встречается редко — я не смог её решить(
zhuharev
09.10.2017 19:58+1нужно авторизироваться на своём компьютере, например, (что бы ip был такой же, как и ip где вы авторизировались с мобильного), а потом просто скопировать cookies на удалённый сервер
MakarkinPRO
А получив это API доступ к Instagram, rtmp случайно не появляется для стриминга?
Ryder95 Автор
Я не знаю, я ещё года 2 назад пытался получить доступ к API, но мне не дали) Его, как мне рассказывали, дают в приоритете каким-либо веб-приложениям, и чаще всего уже запущенным