Reputation: 53
I'm developing an automation API in Node.js with the puppeteer. When the API is called a puppeteer headless browser instance is created and some automation process starts like Login and it logs into the user profile passed in the call. But if the website on which I'm performing automation detects a bot behavior. It sends a login code on the email so a response to that API call is sent asking for the login code. When the login code is submitted in another API call, it simply goes to that page and puts the code. Hence we get logged in. This thing works perfectly when I have only one browser instance. But when multiple browser instances are running the Login code API does not get the required page. It jumps to the last created browser instance which might be performing some other process instead of that targeted instance where login code is required.
Here my question is how I can uniquely identify the browser instances so I can put the login code on that instance instead of some other instance that is performing a different process for a different user.
Start Browser
let browser;
let page;
async function startBrowser() {
if(proxyUrl == '' || proxyUn == '' || proxyPw == ''){
browser = await puppeteer.launch({
//slowMo: 30, // to slowdown the process
headless: false, // set as false to open a chromium
ignoreDefaultArgs: ["--enable-automation"],
defaultViewport: null,
args: ["--no-sandbox",
"--disable-setuid-sandbox",
"--start-maximized",
'--window-size=1920,1080',
"--disable-gpu",
"--disable-dev-profile",
]
});
}else{
//proxy Chain for proxy application
const oldProxyUrl = `http://${proxyUn}:${proxyPw}@${proxyUrl}`;
const newProxyUrl = await proxyChain.anonymizeProxy(oldProxyUrl);
browser = await puppeteer.launch({
//slowMo: 30, // to slowdown the process
headless: false, // set as false to open a chromium
ignoreDefaultArgs: ["--enable-automation"],
defaultViewport: null,
args: ["--no-sandbox",
"--disable-setuid-sandbox",
"--start-maximized",
'--window-size=1920,1080',
"--disable-gpu",
"--disable-dev-profile",
`--proxy-server=${newProxyUrl}`,
]
});
}
browser.on('disconnected', function(){
browser = '';
});
page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
page.setUserAgent(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
);
}
Play Test
async function playTest(url, res) {
try {
try{
await startBrowser();
await page.setDefaultNavigationTimeout(32400000);
}catch(e){
proxyFailedFlag = true;
console.error(new Date().toLocaleString() + ': ', e);
return res.status(400).json({ status: 'failure', info: 'Invalid Proxy' }).end();
}
try{
console.log(new Date().toLocaleString() + ': ', 'connecting login page ...');
await page.goto(url);
}catch(e){
proxyFailedFlag = true;
await closeBrowser(browser);
console.error(new Date().toLocaleString() + ': ', e);
return res.status(400).json({ status: 'failure', info: 'Invalid Proxy' }).end();
}
await waitTillHTMLRendered(page);
await page.setViewport({
width: 1400,
height: 966
});
await playing(res);
} catch (e) {
console.error(new Date().toLocaleString() + ': ', e);
}
}
Playing
const playing = async (res) => {
console.log(new Date().toLocaleString() + ': ', 'waiting for login form ...');
if (await page.$(ACCEPT_COOKIES_SELECTOR) !== null) {
const accept_elm = await page.$(ACCEPT_COOKIES_SELECTOR);
await accept_elm.click({ clickCount: 1 });
}
await page.waitForSelector(USERNAME_SELECTOR, {
visible: true,
});
await page.click(USERNAME_SELECTOR);
await page.keyboard.type(email);
await page.click(PASSWORD_SELECTOR);
await page.keyboard.type(password);
const m_login_elm = await page.$(M_LOGIN_SELECTOR);
await m_login_elm.click({ clickCount: 1 });
console.log(new Date().toLocaleString() + ': ', 'logging in ...');
try {
await page.waitForNavigation();
let pageURL = page.url();
console.log("page url: ", pageURL)
if (pageURL === 'https://www.targetwebsite.com/feed/') {
loginFailedFlag = false;
let accountElm = await page.$(PROFILE_NAME_SELECTOR);
accountName = await page.evaluate(accountElm => accountElm.textContent, accountElm);
accountName = accountName.trim();
console.log("Login success", accountName);
} else if (pageURL === "https:/www.targetwebsite.com/login-submit") {
console.log("Exception Login Submit Form!");
if (await page.$(NOT_REMEMBER_BTN_SELECTOR) !== null) {
const notRememberBtn = await page.$(NOT_REMEMBER_BTN_SELECTOR);
await notRememberBtn.click({ clickCount: 1 });
} else {
throw new Error('Login fail!');
}
} else {
pageURL = pageURL.split("challenge");
if(pageURL[0]==="https://www.targetwebsite.com/captcha/"){
loginFailedFlag = true;
return res.status(400).json({ status: 'failure', info: 'Captcha' }).end();
}
loginFailedFlag = false;
console.log("Pin code is required");
}
} catch (error) {
loginFailedFlag = true;
return res.status(400).json({ status: 'failure', info: 'Invalid Login' }).end();
}
if (await page.$(PIN_INPUT_SELECTOR) !== null) {
console.log("pin code required")
warnFlag = true
warnmsg = 'Awaiting IP Code';
} else {
warnFlag = false
console.log('not found Pin Code step');
await waitTillHTMLRendered(page);
if (await page.$(RE_ACCEPT_COOKIES_SELECTOR) !== null) {
const accept_elm = await page.$(RE_ACCEPT_COOKIES_SELECTOR);
await accept_elm.click({ clickCount: 1 });
}
await getCookies();
}
}
When the Login code is submitted in the second API call
async function handleIpProcess(ip_email, ip_code) {
await waitTillHTMLRendered(page);
await page.click(PIN_INPUT_SELECTOR);
await page.keyboard.type(ip_code);
await page.waitForSelector(PIN_SUBMIT_SELECTOR, {
visible: true,
});
await page.click(PIN_SUBMIT_SELECTOR);
await getCookies();
}
Upvotes: 0
Views: 848
Reputation: 53
I resolved this by creating an object for each instance with a uuid and pushing it to an array.
browser = {
uuid: email, //used email because it was most suitable in my case
pass: password,
instance: undefined,
page: undefined
}
browserInstances.push(browser); //browserInstances is an array initialized earlier in the code
Upvotes: 1