This project uses Selenium and Quartz scheduler to repeatedly like a specific amount of Instagram photos. We can configure the number of photos we want to like each time, how many times the application will run, the time interval between each run and the hashtags that we want to like photos from. Using Selenium, we establish a connection, login with our credentials, and navigate to the hashtags we want.
Implementation
Selenium
First of all we need to build the selenium connector for accessing and interacting with the instagram page. The first thing we should do, is to initialize the web driver. I personally prefer the Chrome driver, but I have made two methods for driver initialization, one for Chrome and one for Firefox.
InstaConnector.java
/** * Initializes the Firefox web driver. * * @return the Web driver */ private WebDriver initializeFirefoxDriver() { System.setProperty("webdriver.gecko.driver", Authentication.GECKO_DRIVER_PATH); WebDriver driver = new FirefoxDriver(); driver.get(Constants.INSTA_URL); driver.manage().window().maximize(); return driver; } /** * Initializes the Chrome web driver. * * @return the Web driver */ private WebDriver initializeChromeDriver(){ System.setProperty("webdriver.chrome.driver", Authentication.CHROME_DRIVER_PATH); WebDriver driver = new ChromeDriver(); driver.get(Constants.INSTA_URL); driver.manage().window().maximize(); return driver; }
After that, we setup the wait object, which is used to wait for elements to satisfy some specific conditions. We set the timeout to 30 seconds.
Now, we ve finished with the initialization and its time to perform the first action. In the login method, we redirect to the instagram page and perform the login with our credentials.
InstaConnector.java
/** * Performs a login. * * @param driver * @param wait */ private void login(WebDriver driver, WebDriverWait wait) { // Login link wait.until(ExpectedConditions .elementToBeClickable(By.xpath(Locators.LOGIN_LINK))); driver.findElement(By.xpath(Locators.LOGIN_LINK)).click(); // Fill username driver.findElement(By.name(Locators.USERNAME)) .sendKeys(Authentication.USERNAME_INPUT); // Fill password driver.findElement(By.name(Locators.PASSWORD)) .sendKeys(Authentication.PASSWORD_INPUT); // Click login button driver.findElement(By.xpath(Locators.LOGIN_BUTTON)).click(); // Wait until home page loading wait.until(ExpectedConditions .visibilityOfElementLocated(By.className("coreSpriteDesktopNavLogoAndWordmark"))); }
After the login we are located to instagram home page. From here we follow the next flow:
- Divide the number of total likes with the hashtags to find how many likes should we perfom in each hashtag.
- Navigate to each hashtag.
- Like the requested number of photos.
Notes:
- We start from the tenth photo in each hashtag page, because the first nine are the most popular and it’s pointless to like them.
- If we find an already liked photo, we skip it.
- If we skip 5 photos consecutively, we have probably visited this section before and we skip the hashtag.
InstaConnector.java
/** * * Navigate to Hashtags. Like photos. * * @param driver * @param wait * @param totalLikes * @param hashtags * @return * @throws InterruptedException */ private int navigateAndLikePhotos(WebDriver driver, WebDriverWait wait, final int totalLikes, final List<String> hashtags) throws InterruptedException{ // Calculate how many likes per hashtag final int likesPerHashtag = totalLikes / hashtags.size(); // Total Like Counter int totalPhotosLiked = 0; for (String hashtag : Constants.HASHTAGS) { System.out.println("Enter hashtag : " + hashtag); // Navigate to Hashtag and enter the first photo navigate(driver, wait, hashtag); // Like photos totalPhotosLiked += likeAndNext(driver, wait, likesPerHashtag); System.out.println(hashtag + " finished. Liked " + totalPhotosLiked + " so far."); if (totalPhotosLiked >= totalLikes){ break; } } return totalPhotosLiked; } /** * Performs the needed navigation. * * @param driver * @param wait * @param hashTag * @throws InterruptedException */ private void navigate(WebDriver driver, WebDriverWait wait, String hashTag) throws InterruptedException { driver.navigate().to(Constants.INSTA_URL + Constants.TAG_PATH + hashTag); // Click first photo wait.until(ExpectedConditions.elementToBeClickable(By.xpath(Locators.FIRST_PHOTO))); WebElement firstImage = driver.findElement(By.xpath(Locators.FIRST_PHOTO)); firstImage.click(); } /** * Like the photo and navigate to the next one * * @param driver * @param wait * @param numberOfPhotos * @throws InterruptedException */ private int likeAndNext(WebDriver driver, WebDriverWait wait, int numberOfPhotos) throws InterruptedException { // Like Counter int photosLiked = 0; // Number of photos that were already clicked in a row int consecutivelyErrors = 0; // Photos entered counter int photosEntered = 0; do { Thread.sleep(1000); // Find heart wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(Locators.HEART_ICON))); WebElement heart = driver.findElement(By.xpath(Locators.HEART_ICON)); String classname = heart.getAttribute("class"); // Click the heart if it's not already clicked if (classname.contains("coreSpriteHeartOpen")) { wait.until(ExpectedConditions.elementToBeClickable(By.xpath(Locators.HEART_LINK))); driver.findElement(By.xpath(Locators.HEART_LINK)).click(); // Increase like counter photosLiked++; //Zero the error counter consecutivelyErrors = 0; System.out.println(photosEntered + ". Heart clicked!"); } // Heart was already clicked else { consecutivelyErrors++; System.out.println(photosEntered + ". Heart was already clicked."); if (consecutivelyErrors == 5) { System.out.println("Five photos in a row were already clicked."); break; } } // Click next button wait.until(ExpectedConditions.elementToBeClickable(By.xpath(Locators.NEXT_BUTTON))); driver.findElement(By.xpath(Locators.NEXT_BUTTON)).click(); photosEntered++; } while (photosLiked < numberOfPhotos); return photosLiked; }
Scheduler
By using the Quartz Scheduler, we can schedyle our app to run many times sequentially. We just have to create a Job to trigger the Connector to run, and we do that in the LikeBatchJob class, which implements the Job interface.
LikeBatchJob.java
public class LikeBatchJob implements Job { InstaConnector instaConnector = new InstaConnector(); public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { instaConnector.start(); } }
Then we only need to schedule this Job to run as many times as we want using a configured time interval. This happens in the InstaScheduler class, which is also our application’s starting point. There, we create the Job instance, a trigger which contains information like the time interval and the repeat count, and start the scheduler.
InstaScheduler.java
public static void scheduleJob() throws SchedulerException { JobDetail job = JobBuilder.newJob(LikeBatchJob.class).withIdentity("likeBatchJob", "group1").build(); Trigger trigger = TriggerBuilder .newTrigger() .withIdentity("likeBatchTrigger", "group1") .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(Constants.MINUTES).withRepeatCount(Constants.REPEAT_COUNT)) .build(); SchedulerFactory schedulerFactory = new StdSchedulerFactory(); org.quartz.Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); scheduler.scheduleJob(job, trigger); }
On constants class we can set up our preferences for the app. Here we set:
- The hashtags we want to like.
- The interval in minutes for the scheduler.
- The number of photos to like each time the app runs.
- The repeat count for the scheduler.
Constants.java
public class Constants { /* Generic */ public final static String INSTA_URL = "https://www.instagram.com/"; public final static String TAG_PATH = "explore/tags/"; public final static Long TIMEOUT_30 = 30L; /* Hashtags */ public final static String DOGS = "dogs"; public final static String CATS = "cats"; public final static String HORSES = "horses"; public final static List<String> HASHTAGS = Collections.unmodifiableList(Arrays.asList(DOGS, CATS)); /* Number of likes */ public final static Integer NUMBER_OF_PHOTOS_TO_LIKE = 10; /* Scheduler */ public final static Integer MINUTES = 5; public final static Integer REPEAT_COUNT = 2; }
Finally, on Authentication.java class we set our Instagram credentials. Also we need to declare the path to the browser driver that we will use.
Authentication.java
public class Authentication { public final static String USERNAME_INPUT = "XXX"; public final static String PASSWORD_INPUT = "XXX"; public final static String CHROME_DRIVER_PATH = "XXX"; }
Installation and Run
- Download a browser driver. You can donwload the Chrome driver for here : Chrome Driver
- On Authentication.java fill your Instagram credentials and the driver’s path.
- On Constants.java setup the requested hashtags, the repeat count, the time interval and the number of photos to like.
- Run the main method on InstaSchedule.java class.