Yet Another Instagram Bot

Romario Fitzgerald
5 min readDec 21, 2020
The InstaBot Code

So here we go again!

But wait, something’s different this time…this bot is actively being used in a production environment :).

You know what that means? It actually works without any mods!

So before we get started, let me explain what the reasons behind this bot are and the objective.

So first Objectives:

  1. Login to Instagram using Headless Chrome
  2. Fetch Account and Posts Data from Instagram
  3. Follow Private Accounts to get Access to their Posts

Now…Why?

Instagram no longer allows anonymous views to account pages, I suspect this is due to the increased traffic since the pandemic.

So regular HTTP requests no longer do the job when trying to get data from and Instagram Account.

We access users Instagram accounts and pull information from them in order to provide additional content for marketing, it’s a great way to reuse existing content to drive engagement.

Why not just use the Instagram API?

To be honest, I‘m not fond of Facebooks developer portal processes and it’s just easier to do things this way. It is as bad as it sounds, it’s easier to create a bot than to use their API.

So Let’s Dig In

I decided to go with a Docker setup in order to make the deployment more portable and also, so that anyone can use it right away.

I’ll walk through without the Dockerfile first.

Step 1: Install Node & NPM

# This installs NodeJS
curl -sL https://deb.nodesource.com/setup_14.x | bash -
apt-get install -y nodejs
# This installs NPM
apt-get install -y npm

Step 2: Install The Puppeteer Dependencies

# These are just Dependencies for Puppeteer
apt-get update && apt-get install -yq libgconf-2-4 \
gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 \
libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 \
libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 \
libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 \
libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 \
libxrender1 libxss1 libxtst6 ca-certificates \
fonts-liberation libappindicator1 libnss3 lsb-release \
xdg-utils wget

Step 3: Install Chromium

# This installs and sets up Chromium for use with Puppeteerapt-get update && apt-get install -y wget --no-install-recommends \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get purge --auto-remove -y curl \
&& rm -rf /src/*.deb

Step 4: Install the Dependencies

npm i puppeteer
npm i express

Step 5: Start Coding

We’ll create our file “server.js”

# First We'll require the dependencies we need
const fs = require('fs');
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
# Next We'll set some global variables we'll need
const USERNAME = 'USERNAME';
const PASSWORD = 'PASSWORD';
const PORT = 3006;
let browser, page;

Step 6: Setting Up Our Browser

We’ll create a function called setup, which will start the browser instance.
It will start the browser, with a userDataDir (User Data Directory) to persist the session data. It will also restart when the browser is disconnected for any reason.

const setup = async () => {    
let launchOptions = { headless: true, userDataDir: "./puppeteer_data", args: ['--no-sandbox','--start-maximized', '--disable-dev-shm-usage']};
browser = await puppeteer.launch(launchOptions);
console.log('Browser Started');
browser.on('disconnected', setup);
};

Step 6: Let’s login to Instagram

This functions sole purpose is to just fetch the instagram login page and sign us in using the USERNAME and PASSWORD constants we set up above.

const login = async(page) => {        
console.log('Logging In:', page.url());
await page.goto('https://www.instagram.com/accounts/login/');
await page.waitFor('input[name="username"]');
await page.focus('input[name="username"]');
await page.keyboard.type(`${USERNAME}`);
await page.focus('input[name="password"]');
await page.keyboard.type(`${PASSWORD}`);
await page.click('button[type="submit"]');
await new Promise(r => setTimeout(r, 2500));
}

Step 6: Saving Cookies for later.

Now that we’re logged in, we should save our cookies. This will keep us from having to login on each request, thus making the service faster. It will also ensure that we won’t have account issues with Instagram, it’ll look suspicious if you’re constantly being logged in.

async function writeCookies(page, cookiesPath) {  
const client = await page.target().createCDPSession();
// This gets all cookies from all URLs, not just the current URL
const cookies = (await client.send("Network.getAllCookies"))["cookies"];
console.log("Saving", cookies.length, "cookies");
fs.writeFileSync(cookiesPath, JSON.stringify(cookies));
}

Step 6: Time to eat some Cookies.

We’ll also need a way to restore our cookies so we can use them.

async function restoreCookies(page, cookiesPath) {  
try {
let buf = fs.readFileSync(cookiesPath);
let cookies = JSON.parse(buf);
console.log("Loading", cookies.length, "cookies into browser");
await page.setCookie(...cookies);
} catch (err) {
console.log("restore cookie error", err);
}
}

Step 7: Let’s Browse!

We‘ve got everything we need to now browser Instagram freely, so let’s do just that and visit an account.

app.get('/fetch', async function (req, res) {    
let handle = req.query.handle;
page = await browser.newPage();
await page.setViewport({width: 1366, height: 768});

await page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36');

restoreCookies(page, './cookies.json');
console.log('CRAWLING PROFILE', handle);
await page.goto(`https://instagram.com/${handle}/?__a=1`);

if (page.url() != `https://www.instagram.com/${handle}/?__a=1`) {
login(page);
}

if (page.url() != `https://www.instagram.com/${handle}/?__a=1`) {
console.log('NAVIGATIN TO HANDLE PAGE');
await page.goto(`https://www.instagram.com/${handle}/?__a=1`);
}
writeCookies(page, './cookies.json');
console.log('PAGE', page.url());
let data = await page.evaluate(() => {
return JSON.parse(document.querySelector("body").innerText);
});

res.json(data);
});

Step 7: Let’s Follow An Account!

The last thing we’ll need to do on Instagram is follow an account, all we have to do is run a click event on the follow button on the users account page:

app.get('/fetch', async function (req, res) {    
let handle = req.query.handle;
page = await browser.newPage();
await page.setViewport({width: 1366, height: 768});

await page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36');

restoreCookies(page, './cookies.json');
console.log('FOLLOWING PROFILE', handle);
await page.goto(`https://instagram.com/${handle}/?__a=1`);

if (page.url() != `https://www.instagram.com/${handle}/?__a=1`) {
login(page);
}

if (page.url() != `https://www.instagram.com/${handle}/?__a=1`) {
console.log('NAVIGATIN TO HANDLE PAGE');
await page.goto(`https://www.instagram.com/${handle}/?__a=1`);
}
writeCookies(page, './cookies.json'); # This is where we click the follow button
await page.click('span > button');
await new Promise(r => setTimeout(r, 500));

res.json({state: 'ok', followed: true});
});

Step 8: The Finalé

We just need to start our server along with the browser and we’re good to go!

setup();
app.listen(PORT);

That’s really all it takes, a pretty simple, straightforward, but very useful application.

Let me know if you have any questions or suggestions in the comments.

Thanks for reading!

Github Repo Link: https://github.com/SirFitz/instabot

--

--

Romario Fitzgerald

I’m a young software developer and entrepreneur who is always looking for ways to grow.