instagrampuppeteerinstagram-apisocial-media

How to trick Instagram into thinking that I use a mobile device using puppeteer?


So basically what I'm trying to do is an app that can make posts to multiple different social media simultaneously. I don't have the time to try to reverse-engineer the Instagram API and I don't think I'm competent enough to do this, so I decided to go with puppeteer (headless browser like phantomjs).

The thing is, that I need the mobile version of Instagram in order to upload photos. I have tried to change the user agent. When I change it in my regular chrome everything works, and I get the mobile version of the website. But when I try the same thing with puppeteer I get the PC version. Here is the code for changing the user agent:

await page.setExtraHTTPHeaders({
   'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
});

Maybe the problem is this piece of code, but when I check the headers in the browser everything is okay. The user agent is the one I need. Maybe the problem is with Instagram itself, I don't know. It just looks Instagram refuses to believe that the requests are coming from a mobile device. I'm confused.

Here is the full version of the code:

const puppeteer = require('puppeteer');
const jsonfile = require('jsonfile');
const fs = require('fs');

const username = 'MYUSERNAME';
const password = 'MYPASSWORD';

async function main(username, password) {
    const browser = await puppeteer.launch({  // launch browser
        headless: false,
        args: [
            `--window-size=${250},${600}`, 
            `--use-fake-ui-for-media-stream`
        ],
        defaultViewport: null
    });

    const page = await browser.newPage();
    await page.setExtraHTTPHeaders({
        'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
    });

    await page.goto('https://instagram.com', { waitUntil: 'networkidle0', timeout: 0 }); // wait until page load

    console.log("Trying to log in");
    const inputs = await page.$$('.f0n8F input');    
    await inputs[0].type(username);
    await inputs[1].type(password);
    
    await Promise.all([     
        page.click('.L3NKy'),
        page.waitForNavigation({ waitUntil: 'networkidle0' }),
    ]);

    //await browser.close();
    await console.log("Done!");
}

main(username, password);

A huuuuuge thanks to everyone who can help even a little bit


Solution

  • The closest you can get to emulate a real mobile device is if you use puppeteer.devices with page.emulate. It is the same experience as you'd achieve when you emulate a device directly from chrome DevTools. You don't need to set the user agent once more, it is set in the DeviceDescriptors.

    The list of available devices can be also checked in the source of DeviceDescriptors.

    For example:

    const puppeteer = require('puppeteer');
    const iPhone = puppeteer.devices['iPhone 6'];
    
    (async () => {
      const browser = await puppeteer.launch();
      const page = await browser.newPage();
      await page.emulate(iPhone);
      await page.goto('https://instagram.com', { waitUntil: 'networkidle0', timeout: 0 }); // wait until page load
    
      // do stuff
      await browser.close();
    })();
    

    FYI: I am pretty sure instagram doesn't encourage web scraping as it doesn't allow reverse engineering of their api neither. You can find yourself banned from the site easily. So make sure you are fully aware of your options.