Задача
Пишем программу для продвижения профиля в Instagram на языке программирования Java.
Задача: Разработать бота на языке программирования Java, который ставит лайки под фотографиями выбранного пользователя в Instagram.
Мы хотим, чтобы написанная нами программы умела открывать нужный нам профиль пользователя и ставить лайки на все фотографии подряд.
Поехали
Далее будет поэтапно описан процесс решения поставленной задачи. Если Вам интересно сразу насладиться результатом — милости прошу в конец статьи. Ссылку на полный текст исходного кода можно найти там же.
Подготовка
К решению любой задачи следует подходить последовательно. Для лайк-кодинга нам понадобится среда разработки на языке Java. Берём самую лучшую (по моему скромному мнению) — IntelliJ IDEA — разработка российской компании JetBrains. Для наших скромных целей достаточно версии Community Edition, она распространяется бесплатно. Скачать можно с официального сайта, установка тривиальна.
Также понадобится браузер — используем Google Chrome. И катастрофически необходим драйвер — специальная программа для управления браузером при помощи другой программы. Скачать можно также с официального сайта. Всё описанное доступно и выглядит аналогично для любой настольной операционной системы — Linux, Mac OS или Windows.
Создание проекта
Открываем IntelliJ, создаём Java-приложение с системой сборки Maven без использования архетипов. Папку для проекта можно выбрать любую, проект мы назовём instalike. Также задаем groupId и artifactId на (instalike и codemika в нашем случае).
В папке src создаём класс Main — основной класс приложения, с точкой входа в программу (функция main). Отсюда она начинает работать.
Просим программу поздороваться с нами и запускаем, чтобы проверить, что всё работает.
Программа вывела сообщение и завершилась с кодом 0. Всё хорошо, можно двигаться дальше.
Где живёт логика
Весь алгоритм поведения нашей программы выносим в отдельный класс —создаём его аналогичным образом в той же папке и называем Instalike. Точно понадобится конструктор с параметрами (логин и пароль), метод start() и много чего ещё, будем дописывать по мере надобности. Ещё понадобится метод log(), чтобы сообщать наружу о том, что происходит внутри (используем стандартный механизм Java).
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Instalike.
* Для работы требуется Google Chrome и Selenium с драйвером.
*/
class Instalike {
private final String login;
private final String password;
private Logger logger;
/**
* @param login Логин.
* @param password Пароль.
*/
Instalike(String login, String password) {
this.login = login;
this.password = password;
logger = Logger.getLogger(Instalike.class.getName());
}
/**
* Начинаем лайк-кодинг!
*/
void start() {
log("Instalike начал свою работу!");
}
/**
* Логируем сообщение.
* @param message Текст сообщения.
*/
private void log(String message) {
logger.log(Level.INFO, String.format("Instalike: %s", message));
}
/**
* Логируем предупреждение.
* @param message Текст предупреждения.
*/
private void warn(String message) {
logger.log(Level.WARNING, String.format("Instalike: %s", message));
}
}
Создаём объект типа Instalike в Main и вызываем метод start(). Также создаём метод setup(), где будем размещать код по настройке программы.
/**
* Самый главный класс.
*/
public class Main {
/**
* Точка входа в программу.
*/
public static void main(String[] args) {
setup();
System.out.println("Торжественно клянусь, что замышляю только шалость! (с)");
var bot = new Instalike(
"login",
"pass"
);
bot.start();
}
/**
* Настройка программы перед работой.
*/
private static void setup() {
// Настройка лога.
System.setProperty(
"java.util.logging.SimpleFormatter.format",
"%1$tF %1$tT [%4$s]: %5$s%6$s%n"
);
}
}
Всё запускаем, проверяем, что работает.
Настройки программы
Настройки — имя пользователя и пароль — сохраняем в файл credentials.properties в ресурсах приложения. Создать его можно также, как и Main с Instalike.
Файл будет выглядеть следующим образом:
login=username
password=pass
В дальнейшем username и pass следует заменить на действительные логин и пароль. Для чтения настроек из файла во время работы программы создадим дополнительный класс, использующий встроенные средства языка Java:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class CredentialProperties {
private final static String fileName = "credentials.properties";
private String login;
private String password;
CredentialProperties() throws IOException {
InputStream inputStream = null;
try {
Properties props = new Properties();
inputStream = getClass().getClassLoader().getResourceAsStream(fileName);
if (inputStream != null) {
props.load(inputStream);
}
else {
throw new FileNotFoundException(String.format("Не удалось загрузить файл %s", fileName));
}
login = props.getProperty("login");
password = props.getProperty("password");
} catch (Exception ex) {
System.out.println(ex.getMessage());
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
}
Файлы Main и Instalike модифицируем соотвественно:
import java.io.IOException;
/**
* Самый главный класс.
*/
public class Main {
private static CredentialProperties credentials;
/**
* Точка входа в программу.
*/
public static void main(String[] args) {
setup();
System.out.println("Торжественно клянусь, что замышляю только шалость! (с)");
var bot = new Instalike(
credentials.getLogin(),
credentials.getPassword()
);
bot.start();
}
/**
* Настройка программы перед работой.
*/
private static void setup() {
// Настройка лога.
System.setProperty(
"java.util.logging.SimpleFormatter.format",
"%1$tF %1$tT [%4$s]: %5$s%6$s%n"
);
// Чтение параметров из файла.
try {
credentials = new CredentialProperties();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
В Instalike добавляем метод login(), который будет использовать полученные логин и пароль.
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Instalike.
* Для работы требуется Google Chrome и Selenium с драйвером.
*/
class Instalike {
private final String login;
private final String password;
private Logger logger;
/**
* @param login Логин.
* @param password Пароль.
*/
Instalike(String login, String password) {
this.login = login;
this.password = password;
logger = Logger.getLogger(Instalike.class.getName());
}
/**
* Начинаем лайк-кодинг!
*/
void start() {
log("Instalike начал свою работу!");
login();
}
/**
* Логинимся.
*/
private void login() {
log(String.format("Рабатаем; Логин: %s, Пароль: %s.", login, password));
}
/**
* Логируем сообщение.
* @param message Текст сообщения.
*/
private void log(String message) {
logger.log(Level.INFO, String.format("Instalike: %s", message));
}
/**
* Логируем предупреждение.
* @param message Текст предупреждения.
*/
private void warn(String message) {
logger.log(Level.WARNING, String.format("Instalike: %s", message));
}
}
Всё запускаем, проверяем, что данные подтягиваются из файла и передаются в Instalike.
Сделано это для того, чтобы:
- Я мог менять логин и пароль, не показывая их на трансляции.
- Готовой программой можно было пользоваться без пересборки, просто меня настройки, которые хранятся отдельно от исходного кода программы.
Управляем браузером из кода
Нужно научиться заставлять браузер открывать сайт, который нам нужен. Подключаем библиотеку Selenium. Это библиотека (кем-то написанная программа, которую можно использовать в своей программе), которая используется для тестирования веб-приложений в реальных, боевых условиях. С её помощью можно писать сценарии, которые управляют браузером, полностью симулируя поведения пользователя. Для использования необходимо добавить блок с зависимостью в файле pom.xml.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>instalike</groupId>
<artifactId>codemika</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>10</source>
<target>10</target>
</configuration>
</plugin>
</plugins>
</build>
<!-- Блок с зависимостью от библиотеки Selenium -->
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.14.0</version>
</dependency>
</dependencies>
<!-- Конец блока -->
</project>
Это как раз Maven-конфигурация, о которой упомянуто выше. Ещё в настройках программы надо указать путь к драйверу. Делается это кодом в методе setup() в классе Main.
/**
* Настройка программы перед работой.
*/
private static void setup() {
// Настройка лога.
System.setProperty(
"java.util.logging.SimpleFormatter.format",
"%1$tF %1$tT [%4$s]: %5$s%6$s%n"
);
// Чтение параметров из файла.
try {
credentials = new CredentialProperties();
}
catch (IOException ex) {
ex.printStackTrace();
}
// Указываем путь к Chromedriver.
System.setProperty(
"webdriver.chrome.driver",
"Path:\To\Chrome\driver\chromedriver.exe"
);
}
В методе start() cоздаём экземпляр драйвера браузера. В методе login()при помощи browser.get() открываем, например, сайт Кодемики.
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Instalike.
* Для работы требуется Google Chrome и Selenium с драйвером.
*/
class Instalike {
private final String login;
private final String password;
private ChromeDriver browser;
private Logger logger;
/**
* @param login Логин.
* @param password Пароль.
*/
Instalike(String login, String password) {
this.login = login;
this.password = password;
logger = Logger.getLogger(Instalike.class.getName());
}
/**
* Начинаем лайк-кодинг!
*/
void start() {
log("Instalike начал свою работу!");
browser = new ChromeDriver();
login();
}
/**
* Логинимся.
*/
private void login() {
browser.get("https://www.comemika.ru/");
log("Привет, Кодемика!");
}
/**
* Логируем сообщение.
* @param message Текст сообщения.
*/
private void log(String message) {
logger.log(Level.INFO, String.format("Instalike: %s", message));
}
/**
* Логируем предупреждение.
* @param message Текст предупреждения.
*/
private void warn(String message) {
logger.log(Level.WARNING, String.format("Instalike: %s", message));
}
}
Всё запускаем, проверяем, что поведение соответствует ожидаемому.
Замечаем, что в новом окне браузера есть плашка, где написано, что браузером управляет тестовое ПО — это и есть наша программа.
Открываем Instagram
Меняем в методе login() ссылку на страницу входа в Instagram. Повторяем проверку предыдущего пункта. Замечаем, что программа свою работу завершила, а бразуер остался открытым. Это потому что мы его об этом не просили. Добавляем метод end() в Instalike, который делает browser.close() и говорит о том, что работа закончена.
Создаём метод waitASecond() — он приостанавливает выполнение программы на случайный промежуток от 3 до 5 секунд. Это нужно, чтобы симулировать поведение реального пользователя и Instagram ничего не заподозрил. Иначе заставит вводить код из смс. Дальше этот метод будет использоваться после каждого обращения к браузеру.
/**
* Начинаем лайк-кодинг!
*/
void start() {
log("Instalike начал свою работу!");
browser = new ChromeDriver();
login();
end();
}
/**
* Логинимся.
*/
private void login() {
browser.get("https://www.instagram.com/accounts/login/");
waitASecond();
}
/**
* Заканчиваем работу браузера.
*/
private void end() {
browser.close();
log("Нокс!");
}
/**
* Ждём секунду. На самом деле от трёх до пяти, случайным образом.
*/
private void waitASecond() {
try {
Thread.sleep((baseDelay + ThreadLocalRandom.current().nextInt(1, 3)) * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
Запускаем, видим, что программа закрывает вкладку, которую ранее открыла. Но не сразу, а через несколько секунд.
Скажи мне, кто ты, и назови пароль
Теперь необходимо помочь программе отыскать на странице поля для ввода логина и пароля, ввести в них нужные значения и нажать на кнопку «Войти». Воспользуемся встроенной в браузер возможностью просматривать внутренности открытой веб-страницы (правая кнопка мыши, просмотреть код).
У полей ввода заданы уникальные имена: name="username" и name="password"для логина и пароля соотвественно. Воспользуемся этим для поиска их на странице. Кнопку «Войти» отыскать немного сложнее. У неё нет имени, но можно взять путь, указывающий точное размещение кнопки на странице —XPath.
Найденной кнопке делаем click() (почти как «кусь»). Все такие пути (XPath) лучше хранить в отдельном словаре. Создадим его в классе Instalike и используем при поиске кнопки.
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.Map.entry;
/**
* Instalike.
* Для работы требуется Google Chrome и Selenium с драйвером.
*/
class Instalike {
// Словарь путей к элементам сайта.
private final Map xpaths = Map.ofEntries(
entry("login_button", "//*[@id="react-root"]/section/main/div/article/div/div[1]/div/form/div[3]/button")
);
private final String login;
private final String password;
private ChromeDriver browser;
private int baseDelay;
private Logger logger;
/**
* Конструктор.
* @param login Логин.
* @param password Пароль.
*/
Instalike(String login, String password) {
this(login, password, 3);
}
/**
* Конструктор.
* @param login Логин.
* @param password Пароль.
* @param baseDelay Задержка между действиями (в секундах).
*/
Instalike(String login, String password, int baseDelay) {
this.login = login;
this.password = password;
this.baseDelay = baseDelay;
logger = Logger.getLogger(Instalike.class.getName());
}
/**
* Начинаем лайк-кодинг!
*/
void start() {
log("Instalike начал свою работу!");
browser = new ChromeDriver();
login();
end();
}
/**
* Логинимся.
*/
private void login() {
browser.get("https://www.instagram.com/accounts/login/");
waitASecond();
browser.findElement(new By.ByName("username")).sendKeys(login);
waitASecond();
browser.findElement(new By.ByName("password")).sendKeys(password);
browser.findElement(new By.ByXPath(xpaths.get("login_button"))).click();
waitASecond();
log(String.format("Залогинились, как %s", login));
}
/**
* Заканчиваем работу браузера.
*/
private void end() {
browser.close();
log("Нокс!");
}
/**
* Ждём секунду. На самом деле от трёх до пяти, случайным образом решается.
*/
private void waitASecond() {
try {
Thread.sleep((baseDelay + ThreadLocalRandom.current().nextInt(1, 3)) * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
/**
* Логируем сообщение.
* @param message Текст сообщения.
*/
private void log(String message) {
logger.log(Level.INFO, String.format("Instalike: %s", message));
}
/**
* Логируем предупреждение.
* @param message Текст предупреждения.
*/
private void warn(String message) {
logger.log(Level.WARNING, String.format("Instalike: %s", message));
}
}
Запускаем, проверяем, что получается залогиниться. Пользователь и пароль в файле конфигурации к этому моменту должны уже быть действующими.
Он хотел созорничать, но не знал, с чего начать
Теперь необходимо указать программе, какой профиль будем лайк-кодить. Сделаем это через параметр target. Таким образом, необходимо открыть указанную страницу и отыскать первую фотографию в профиле. Поступим аналогичным образом (XPath).
Если элемент удалось найти, то открываем фотографию, иначе сообщаем о том, что ничего не вышло. Все указанные действия выполним в новом методе —findFirst().
/**
* Ищем первую фотографию в профиле.
* @return True, если нашлась, false в противном случае.
*/
private boolean findFirst() {
browser.get(String.format("https://www.instagram.com/%s", target));
waitASecond();
log(String.format("Ставим лайкосы для %s.", target));
try {
browser.findElement(new By.ByXPath(xpaths.get("first_photo"))).click();
waitASecond();
return true;
} catch (NoSuchElementException ex) {
warn("Не могу найти первую фотку! Наверное, фоток нет вообще!");
return false;
}
}
Лайк-кодинг в действии
Приступим к самому интересному. Будем ставить свой кодерский лайк! На странице с открытой фотографией необходимо отыскать кнопку с сердечком, проверить, что лайк ещё не стоит, и поставить его. И всё это мы уже умеем. Оформим логику в новом методе likeCurrent().
/**
* Лайк-кодим открытую фотографию.
*/
private void likeCurrent() {
var likeButton = browser.findElement(new By.ByXPath(xpaths.get("like_button")));
try {
browser.findElement(new By.ByClassName("glyphsSpriteHeart__filled__24__red_5"));
log("Тут лайк уже стоит!");
}
catch (NoSuchElementException ex) {
likeButton.click();
log("Лайкос поставлен, мой генерал!");
waitASecond();
}
}
В нём мы ищем кнопку по новому XPath, сохраняем. Пробуем найти внутри элемент с классом "glyphsSpriteHeart__filled__24__red_5", то есть закрашенное красным сердечко. Если получатеся, то сообщаем, что лайк уже стоит. Иначе берём сохранённую кнопку и делаем ей click(). Сообщаем об успехе, ждём секунду, едем дальше.
Next
Перед тем, как двинуться дальше, нам пригодится ещё один метод. Он будет открывать следующую фотографию. Назовём его getNext().
/**
* Открываем следующую фотографию.
* @return True, если удалось открыть, false в противном случае.
*/
private boolean getNext() {
try {
browser.findElement(new By.ByClassName("coreSpriteRightPaginationArrow")).click();
waitASecond();
return true;
}
catch (NoSuchElementException e) {
warn("Не могу открыть следующее фото. Кажется, закончились.");
return false;
}
}
В данном случае ищем кнопку, класс которой совпадает с "coreSpriteRightPaginationArrow". Это стрелочка вправо на краю страницы. Если нашли — нажимаем.
Последний штрих
Сейчас у нас есть всё необходимое для решения поставленной задачи. Осталось только зациклить процесс, и это можно сделать в методе start().
/**
* Начинаем лайк-кодинг!
*/
void start() {
log("Instalike начал свою работу!");
browser = new ChromeDriver();
login();
if (findFirst()) {
likeCurrent();
while (getNext()) {
likeCurrent();
}
}
end();
}
Таким образом, алгоритм у нас следующий:
- Ищем первую фотографию.
- Если нашли, лайкаем.
- Пока есть возможность листать фотки — листаем и лайкаем.
Запускаем и наслаждаемся слайд-шоу!
Заключение
Эта программа является лишь примером использования языка Java и библиотеки Selenium. Дальнейшая разработка этой программы (или написание любой другой) ограничено лишь Вашей фантазией и навыками программирования. Поэтому приглашаю Вас подтянуть свой скилл в онлайн школе Кодемика. Увидимся на занятиях!
Исходный код программы полностью можно посмотреть и скачать на GitHub.
PP.S. Как запустить готовый код
Всё очень просто! Скачиваем исходный код и разархивируем в папку.
Открываем папку в IntelliJ.
Вводим свои логин и пароль в файле настроек и указываем путь к Chromedriver в Main
Запускаем.
Успех!
Читайте больше материалов в блоге Кодемики, для этого нужно зарегистрироваться: https://lk.codemika.ru