Trouble figuring out where/when to close browser in puppeteer
Asked Answered
T

0

0

I'm trying to write an application that will take multiframe screenshots of animated banners, sometimes multiple on a page, by doing the following:

  • Create a local folder to store images
  • Open a browser instance at the provided url
  • After the page is loaded, iterate over all of the iframes on the page that match the provided selector and apply them to the banners variable.
  • Apply the x, y, width, height, and id attributes to each banner object
  • Iterate over the banners array and for each banner take a screenshot every .5 seconds for 10 seconds (or whatever values I ultimately decide on).
  • Close the browser

The issue I have here is I can't seem to figure out where and when to close the browser; all of the examples I look at just seem to have the browser close call at the end of everything, but when I do that I get an error message (shown below) that seems to indicate it's closing the browser before screenshots get going. I'm fairly certain the core of the issue is that I'm still working on having a good understanding of async and predicting when certain things will happen in the code.

Error:

TargetCloseError: Protocol error (Target.activateTarget): Session closed. Most likely the page has been closed.

Full code:

const puppeteer = require('puppeteer');
const os = require('os');
const fs = require('fs');
const path = require('path');

const workDir = './temp';
const REFRESH_SELECTOR = '.icon-button.material-icons[aria-label="refresh"]';
const PREVIEW_SELECTOR = '.dynamic-ad-card-back iframe';

const CHROME_PATHS = {
  darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  linux: '/usr/bin/google-chrome',
  win32: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
};
const CHROME_PATH = CHROME_PATHS[os.platform()];
const PIXEL_DENSITY = 2;

(async () => {
    const browser = await puppeteer.launch({
            headless: true,
            executablePath: CHROME_PATH,
            args: [
          '--no-sandbox',
        '--disable-setuid-sandbox',
        '--single-process',
        ],
        });

    if (!fs.existsSync(workDir)) {
      fs.mkdirSync(workDir);
      console.log(`${workDir} created`)
    };

    let frameCount = 0;

    const page = await browser.newPage();
    page.setViewport({width: 1280, height: 6000, deviceScaleFactor: PIXEL_DENSITY});
    await page.goto('https://[URL-OBSCURED]', { waitUntil: 'networkidle0' });
    console.log('page navigation has happened');

    async function getScreenShots() {
        console.log('getScreenShots called');

        const banners = await page.$$eval(PREVIEW_SELECTOR, iframes => {
          return Array.from(iframes, (el) => {
            const {x, y, width, height} = el.getBoundingClientRect();

            return {
              left: x,
              top: y,
              width,
              height,
              id: el.id,
            };
          });
        }, PREVIEW_SELECTOR).catch(e => {
          console.error(e.message);
        });

        for (const banner of banners) {
            console.log(`current banner ID: ${banner.id}`);

            const intervalId = setInterval(async () => {
                // await page.click();
                await page.screenshot({
                    clip: {
                        x: banner.left,
                y: banner.top,
                width: banner.width,
                height: banner.height,
                    },
                    path: `${workDir}/screenshot_${banner.id}-frame_index-${frameCount}.png`,
                }).then(() => {
                    console.log(`generating file: screenshot_${banner.id}-frame_index-${frameCount}.png`);
                    console.log(`current banner: ${banner.id}`);
                    frameCount++
                });
            }, 500);
            frameCount = 0;

            setTimeout(() => {
                clearInterval(intervalId);
            }, 10000);
        }
    }

    await getScreenShots().catch((e) => console.error(e.message));
    await browser.close();
})();
Tory answered 4/8, 2023 at 16:40 Comment(1)
It's hard to help without the site, but I think you're breaking some rules of async/await that likely lead to your page doing operations simultaneously, leading to a race condition. I would use awaitable timeouts rather than callbacks and get rid of .then/catch. Also, make sure to await every promise, like page.setViewport. This isn't about browser.close().Monosaccharide

© 2022 - 2024 — McMap. All rights reserved.