Instalike – Use selenium for Instagram account promotion.

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.