10 апреля 2020

Пишем бота для Instagram на Python

Frontend-разработчик в Foquz. https://www.cat-in-web.ru/
Пошаговое руководство по созданию бота для Instagram на Python, который будет всё делать за вас: лайки, подписки, комментарии, анализ постов... Остаётся лишь фотографировать!
Пишем бота для Instagram на Python

Чтобы охватить большую аудиторию в Instagram, получить больше лайков и новых подписчиков, мы обращаемся за помощью к специалистам: SocialCaptain, Kicksta, Instavast и другим компаниям. У них есть автоматизированные инструменты, которые делают за вас всю работу. За это мы платим большие деньги – но то же самое можно получить бесплатно, используя InstaPy.

Мы напишем бота на Python, который автоматизирует ваши действия в Instagram. В результате вы получите больше лайков и подписчиков с минимальными усилиями. Параллельно разберемся в автоматизации браузера, работе Selenium и шаблона Page Object, лежащих в основе InstaPy.

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

Как устроены боты Instagram

Прежде чем начинать автоматизировать что-либо, давайте подумаем, как получает лайки и подписки в Instagram реальный пользователь?

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

Очень важны настройки бота, ведь от них напрямую зависят результаты, которые вы получите. Важно отобрать те профили, владельцам которых будет интересен ваш контент. Например, если вы продаете в Instagram женскую одежду, будет разумно научить бота отслеживать женские аккаунты, а также посты с хештегами #красота, #мода, #одежда. Это повысит вероятность того, что ваш профиль будет замечен целевой аудиторией.

Но как реализовать это технически? Instagram Developer API ограничен и не подойдет для наших целей. Следует обратиться к браузерной автоматизации.

Это работает очень просто:

  1. Вы указываете свои учетные данные.
  2. Устанавливаете критерии для отбора профилей и постов, а также настройки для комментариев.
  3. Бот открывает браузер, переходит на сайт, авторизуется с вашими данными и начинает выполнять полученные инструкции.

Давайте перейдем к практике. Для начала напишем бота, который сможет самостоятельно зайти в ваш Instagram-профиль.

Автоматизация браузера

Для первой версии бота будем использовать Selenium – инструмент, работающий под капотом InstaPy, к которому мы перейдем чуть позже.

Для начала установите сам Selenium. Убедитесь, что на вашем компьютере установлен браузер Firefox и Firefox WebDriver. Мы будем работать с этим браузером, так как в последней версии InstaPy нет поддержки Chrome.

Для работы Selenium вам также может понадобиться установить geckodriver.

1. Открываем браузер

Создайте файл с расширением .py и вставьте туда следующий код:

instabot.py
        from time import sleep
from selenium import webdriver

browser = webdriver.Firefox()
browser.get('https://www.instagram.com/')
sleep(5)
browser.close()
    

Сначала мы импортируем нужные пакеты. Затем инициализируется драйвер Firefox и запускается браузер. Бот набирает в адресной строке адрес https://www.instagram.com/ – открывается страница авторизации Instagram. Через 5 секунд ожидания браузер закрывается. Запустите код и проверьте, как он работает. Только что мы написали в своем роде "Hello world" на Selenium.

2. Открываем страницу авторизации

Добавим авторизацию. Для начала составим пошаговый алгоритм действий:

  1. Перейти на страницу https://www.instagram.com/.
  2. Нажать ссылку Авторизоваться.
  3. Ввести логин и пароль.
  4. Нажать на кнопку Log In ( Войти).
Примечание
Если по адресу https://www.instagram.com/ сразу открывается страница авторизации и нет ссылок с текстом (Log in/Авторизоваться), просто пропустите этот шаг.

Первый пункт мы уже выполнили. Давайте теперь найдем на странице ссылку для авторизации и кликнем по ней:

instabot.py
        from time import sleep
from selenium import webdriver

browser = webdriver.Firefox()
browser.implicitly_wait(5) # устанавливаем пятисекундную задержку
# Если Selenium не может найти элемент, он ждет, чтобы все загрузилось и пытается снова

browser.get('https://www.instagram.com/')

# Следующие строки говорят боту найти ссылку с текстом Log in и кликнуть по ней. 
login_link = browser.find_element_by_xpath("//a[text()='Log in']")  
login_link.click()

sleep(5)

browser.close()
    

Мы используем для поиска нужной ссылки XPath, но есть и другие методы.

Запустите скрипт и убедитесь, что он работает. В браузере должна открыться страница авторизации.

3. Заполнение формы

В форме авторизации – три важных элемента:

  • Поле для введения логина;
  • Поле для пароля;
  • Кнопка Войти.

Давайте найдем их, введем учетные данные и залогинимся:

instabot.py
        from time import sleep
from selenium import webdriver

browser = webdriver.Firefox()
browser.implicitly_wait(5)

browser.get('https://www.instagram.com/')

login_link = browser.find_element_by_xpath("//a[text()='Log in']")
login_link.click()

sleep(2)

username_input = browser.find_element_by_css_selector("input[name='username']")
password_input = browser.find_element_by_css_selector("input[name='password']")

username_input.send_keys("<имя пользователя>")
password_input.send_keys("<пароль>")

login_button = browser.find_element_by_xpath("//button[@type='submit']")
login_button.click()

sleep(5)

browser.close()
    

Устанавливаем двухсекундную задержку для загрузки страницы. Находим и заполняем нужные поля. В конце ищем и нажимаем кнопку для входа.

Если вы укажете правильные данные и запустите этот скрипт, он самостоятельно авторизуется в вашем Instagram аккаунте.

***

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

Хорошая новость – все эти шаги за вас может сделать InstaPy. Но прежде чем мы начнем с ним работать, давайте разберемся в основах – паттерне Page Object.

Шаблон Page Object

Мы написали код для авторизации – но как теперь его тестировать ? Это могло бы выглядеть как-то так:

        def test_login_page(browser):
    browser.get('https://www.instagram.com/accounts/login/')
    username_input = browser.find_element_by_css_selector("input[name='username']")
    password_input = browser.find_element_by_css_selector("input[name='password']")
    username_input.send_keys("<your username>")
    password_input.send_keys("<your password>")
    login_button = browser.find_element_by_xpath("//button[@type='submit']")
    login_button.click()

    errors = browser.find_elements_by_css_selector('#error_message')
    assert len(errors) == 0
    

Что не так с этим кодом? Он не соответствует принципу DRY и идеям чистого кода: одни и те же фрагменты дублируются и в приложении, и в тесте.

В этом контексте дублирование кода особенно плохо, так как Selenium зависит от элементов пользовательского интерфейса, а они имеют тенденцию меняться. Если это происходит, хотелось бы вносить изменения только в одном месте, а не в десятке. Здесь и приходит на помощь шаблон Page Object.

С помощью этого шаблона вы создаете классы page objects для наиболее важных страниц или фрагментов страницы, которые предоставляют удобные интерфейсы для взаимодействия.

Мы можем отрефакторить наш код и создать класс HomePage и класс LoginPage:

pages.py
        from time import sleep

class LoginPage:
    def __init__(self, browser):
        self.browser = browser

    def login(self, username, password):
        username_input = self.browser.find_element_by_css_selector("input[name='username']")
        password_input = self.browser.find_element_by_css_selector("input[name='password']")
        username_input.send_keys(username)
        password_input.send_keys(password)
        login_button = browser.find_element_by_xpath("//button[@type='submit']")
        login_button.click()
        sleep(5)

class HomePage:
    def __init__(self, browser):
        self.browser = browser
        self.browser.get('https://www.instagram.com/')

    def go_to_login_page(self):
        self.browser.find_element_by_xpath("//a[text()='Log in']").click()
        sleep(2)
        return LoginPage(self.browser)
    

Логика осталась без изменений, только теперь домашняя страница и страница входа представлены в виде классов. Они инкапсулируют все действия, необходимые для поиска и обработки данных в пользовательском интерфейсе.

Обратите внимание, при переходе на другую страницу с помощью метода объекта страницы, возвращается новый объект страницы. Взгляните на возвращаемое значение функции go_to_log_in_page().

Если бы у нас уже был класс FeedPage, то метод login() класса LoginPage вернул бы экземпляр страницы фида (return FeedPage()).

Изменим основной код:

instabot.py
        from selenium import webdriver
from pages import HomePage

browser = webdriver.Firefox()
browser.implicitly_wait(5)

home_page = HomePage(browser)
login_page = home_page.go_to_login_page()
login_page.login("<your username>", "<your password>")

browser.close()
    

Теперь программа выглядит намного проще и понятнее. Тесты тоже можно переписать:

        def test_login_page(browser):
    home_page = HomePage(browser)
    login_page = home_page.go_to_login_page()
    login_page.login("<your username>", "<your password>")

    errors = browser.find_elements_by_css_selector('#error_message')
    assert len(errors) == 0
    

Если в интерфейсе что-то изменится, не придется менять тесты – и это правильно.

Чтобы узнать больше о шаблоне Page Object, обратитесь к официальной документации и статье Мартина Фаулера.

А мы переходим к созданию бота версии 2.0 – с помощью InstaPy.

Создание бота с помощью InstaPy

Для начала установим InstaPy:

        $ python3 -m pip install instapy

    
Виртуальные среды
Хорошей практикой является использование для каждого проекта отдельных виртуальных сред. Это позволяет изолировать зависимости.

Авторизация в Instagram

Перепишем код с использованием InstaPy:

instabot2.py
        from instapy import InstaPy

InstaPy(username="<your_username>", password="<your_password>").login()
    

Подставьте правильный логин и пароль и запустите скрипт. Вуаля! Одна строчка кода – а результат тот же самый!

На самом деле, не тот же самый. Instapy выполняет множество других действий: проверяет интернет-соединение, состояние серверов Instagram и пр. Все это вы можете наблюдать напрямую в браузере, а также в логах.

        INFO [2019-12-17 22:03:19] [username]  -- Connection Checklist [1/3] (Internet Connection Status)
INFO [2019-12-17 22:03:20] [username]  - Internet Connection Status: ok
INFO [2019-12-17 22:03:20] [username]  - Current IP is "17.283.46.379" and it's from "Germany/DE"
INFO [2019-12-17 22:03:20] [username]  -- Connection Checklist [2/3] (Instagram Server Status)
INFO [2019-12-17 22:03:26] [username]  - Instagram WebSite Status: Currently Up
    

Неплохо для одной строки кода, правда? Переходим к решительным действиям!

Основные настройки бота

Предположим, что ваш профиль посвящен автомобилям. Вы хотите, чтобы бот взаимодействовал с профилями людей, интересующихся автомобилями.

Лайки

Например, бот может лайкать посты с хештегами #bmw или #mercedes. Для этого предназначен метод like_by_tags():

instabot2.py
        from instapy import InstaPy

session = InstaPy(username="<your_username>", password="<your_password>")
session.login()
session.like_by_tags(["bmw", "mercedes"], amount=5) # [1]
    

Мы передаем методу массив тегов, которые нужно лайкнуть, и количество постов для каждого тега. Другими словами, бот должен поставить 10 лайков – по пять для каждого из двух тегов.

Но взгляните, что происходит в логах после запуска скрипта:

        INFO [2019-12-17 22:15:58] [username]  Tag [1/2]
INFO [2019-12-17 22:15:58] [username]  --> b'bmw'
INFO [2019-12-17 22:16:07] [username]  desired amount: 14  |  top posts [disabled]: 9  |  possible posts: 43726739
INFO [2019-12-17 22:16:13] [username]  Like# [1/14]
INFO [2019-12-17 22:16:13] [username]  https://www.instagram.com/p/B6MCcGcC3tU/
INFO [2019-12-17 22:16:15] [username]  Image from: b'mattyproduction'
INFO [2019-12-17 22:16:15] [username]  Link: b'https://www.instagram.com/p/B6MCcGcC3tU/'
INFO [2019-12-17 22:16:15] [username]  Description: b'Mal etwas anderes \xf0\x9f\x91\x80\xe2\x98\xba\xef\xb8\x8f Bald ist das komplette Video auf YouTube zu finden (n\xc3\xa4here Infos werden folgen). Vielen Dank an @patrick_jwki @thehuthlife  und @christic_  f\xc3\xbcr das bereitstellen der Autos \xf0\x9f\x94\xa5\xf0\x9f\x98\x8d#carporn#cars#tuning#bagged#bmw#m2#m2competition#focusrs#ford#mk3#e92#m3#panasonic#cinematic#gh5s#dji#roninm#adobe#videography#music#bimmer#fordperformance#night#shooting#'
INFO [2019-12-17 22:16:15] [username]  Location: b'K\xc3\xb6ln, Germany'
INFO [2019-12-17 22:16:51] [username]  --> Image Liked!
INFO [2019-12-17 22:16:56] [username]  --> Not commented
INFO [2019-12-17 22:16:57] [username]  --> Not following
INFO [2019-12-17 22:16:58] [username]  Like# [2/14]
INFO [2019-12-17 22:16:58] [username]  https://www.instagram.com/p/B6MDK1wJ-Kb/
INFO [2019-12-17 22:17:01] [username]  Image from: b'davs0'
INFO [2019-12-17 22:17:01] [username]  Link: b'https://www.instagram.com/p/B6MDK1wJ-Kb/'
INFO [2019-12-17 22:17:01] [username]  Description: b'Someone said cloud? \xf0\x9f\xa4\x94\xf0\x9f\xa4\xad\xf0\x9f\x98\x88 \xe2\x80\xa2\n\xe2\x80\xa2\n\xe2\x80\xa2\n\xe2\x80\xa2\n#bmw #bmwrepost #bmwm4 #bmwm4gts #f82 #bmwmrepost #bmwmsport #bmwmperformance #bmwmpower #bmwm4cs #austinyellow #davs0 #mpower_official #bmw_world_ua #bimmerworld #bmwfans #bmwfamily #bimmers #bmwpost #ultimatedrivingmachine #bmwgang #m3f80 #m5f90 #m4f82 #bmwmafia #bmwcrew #bmwlifestyle'
INFO [2019-12-17 22:17:34] [username]  --> Image Liked!
INFO [2019-12-17 22:17:37] [username]  --> Not commented
INFO [2019-12-17 22:17:38] [username]  --> Not following
    

По умолчанию InstaPy будет лайкать первые девять постов в ленте к дополнению к параметру amount. В этом случае общее количество лайков – 14 на один тег (9 + 5).

InstaPy регистрирует каждое действие: упоминает, какой пост ему понравился, ссылку на него, описание, местоположение, указывает, комментировал ли и подписался ли на аккаунт.

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

Вы также можете указать параметры постов, которые бот НЕ должен лайкать, с помощью метода set_dont_like():

instabot2.py
        from instapy import InstaPy

session = InstaPy(username="<your_username>", password="<your_password>")
session.login()
session.like_by_tags(["bmw", "mercedes"], amount=5)
session.set_dont_like(["naked", "nsfw"])
    

Теперь бот будет игнорировать посты, в описании которых есть слова "naked" или "nsfw" (not safe/suitable for work).

Подписки

Кроме лайков бот умеет подписываться на аккаунты. Для этого предназначена функция set_do_follow():

instabot2.py
        from instapy import InstaPy

session = InstaPy(username="<your_username>", password="<your_password>")
session.login()
session.like_by_tags(["bmw", "mercedes"], amount=5)
session.set_dont_like(["naked", "nsfw"])
session.set_do_follow(True, percentage=50)
    

Если вы запустите этот скрипт, то бот подпишется на 50% от тех юзеров, чьи посты он лайкнул. Опять же каждое действие будет залогировано.

Комментарии

Еще одна опция InstaPy – возможность оставлять комментарии. Для этого нужно сделать две вещи. Сначала разрешите комментирование вызовом метода set_do_comment():

instabot2.py
        from instapy import InstaPy

session = InstaPy(username="<your_username>", password="<your_password>")
session.login()
session.like_by_tags(["bmw", "mercedes"], amount=5)
session.set_dont_like(["naked", "nsfw"])
session.set_do_follow(True, percentage=50)
session.set_do_comment(True, percentage=50)
    

Затем укажите, что именно писать с помощью set_comments():

instabot2.py
        from instapy import InstaPy

session = InstaPy(username="<your_username>", password="<your_password>")
session.login()
session.like_by_tags(["bmw", "mercedes"], amount=5)
session.set_dont_like(["naked", "nsfw"])
session.set_do_follow(True, percentage=50)
session.set_do_comment(True, percentage=50)
session.set_comments(["Nice!", "Sweet!", "Beautiful :heart_eyes:"])
    

Запустите скрипт, и бот оставит один из трех указанных комментариев под половиной постов, с которыми он взаимодействовал.

Закрытие сессии

После того, как вы сделали все, что хотели, нужно закрыть сессию, вызвав метод end():

instabot2.py
        from instapy import InstaPy

session = InstaPy(username="<your_username>", password="<your_password>")
session.login()
session.like_by_tags(["bmw", "mercedes"], amount=5)
session.set_dont_like(["naked", "nsfw"])
session.set_do_follow(True, percentage=50)
session.set_do_comment(True, percentage=50)
session.set_comments(["Nice!", "Sweet!", "Beautiful :heart_eyes:"])
session.end()
    

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

Дополнительные возможности InstaPy

InstaPy – это большой проект, который имеет множество тщательно документированных функций. Рассмотрим несколько наиболее полезных из них.

Квоты

Нельзя скрейпить Instagram целыми днями, сервис быстро это заметит. Поэтому полезно установить квоты на действия бота.

        session.set_quota_supervisor(enabled=True, peak_comments_daily=240, peak_comments_hourly=21)

    

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

Аргумент headless_browser

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

        session = InstaPy(username='test', password='test', headless_browser=True)

    

Обратите внимание, соответствующий флаг устанавливается при инициализации объекта InstaPy.

Искусственный интеллект для анализа сообщений

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

InstaPy-бот можно интегрировать с ClarifApi – инструментом для распознавания изображений и видео:

        session.set_use_clarifai(enabled=True, api_key='<your_api_key>')
session.clarifai_check_img_for(['nsfw'])
    

Теперь бот будет игнорировать любое изображение, которое ClarifApi посчитает NSFW. 5000 обращений в месяц – бесплатно.

Количество подписчиков

Зачастую не имеет смысла взаимодействовать с аккаунтами с большим количеством подписчиков. InstaPy дает возможность установить границы для этого количества, чтобы бот не тратил впустую вычислительные ресурсы вашей машины:

        session.set_relationship_bounds(enabled=True, max_followers=8500)

    

Теперь бот не будет взаимодействовать с постами пользователей, у которых больше 8,5 тысяч подписчиков.

Множество других опций и конфигураций вы можете найти в документации InstaPy.

***

InstaPy – гибкий инструмент, который позволяет легко и быстро автоматизировать действия пользователя в Instagram. Его работа основана на браузерной автоматизации (Selenium) и использовании шаблона Page Object для облегчения работы с веб-страницами.

Если вы еще не начали писать код, прочитайте нашу публикацию о том, как создать виртуальное окружение в Python. Это позволит избежать проблем с зависимостями пакетом. Мы также писали о том, как работает распознавание объектов в реальном времен – впоследствии бота можно улучшить, если привлечь собственную модель для анализа фотографий пользователей.

Больше полезной информации вы можете получить на нашем телеграм-канале «Библиотека питониста». Рекомендуем также обратить внимание на учебный курс по Python от «Библиотеки программиста».

Источники

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Senior Java Developer
Москва, по итогам собеседования
Разработчик С#
от 200000 RUB до 400000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ