Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use puppeteer-core with electron?

I got this code from another Stackoverflow Question:

import electron from "electron";
import puppeteer from "puppeteer-core";

const delay = (ms: number) =>
  new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, ms);
  });

(async () => {
  try {
    const app = await puppeteer.launch({
      executablePath: electron,
      args: ["."],
      headless: false,
    });
    const pages = await app.pages();
    const [page] = pages;

    await page.setViewport({ width: 1200, height: 700 });
    await delay(5000);
    const image = await page.screenshot();
    console.log(image);
    await page.close();
    await delay(2000);
    await app.close();
  } catch (error) {
    console.error(error);
  }
})();

Typescript compiler complains about executablePath property of launch method options object cause it needs to be of type string and not Electron. So how to pass electron chromium executable path to puppeteer?

like image 575
pouya Avatar asked Oct 03 '19 06:10

pouya


1 Answers

You cannot use electron executable with Puppeteer directly without some workarounds and flag changes. They have tons of differences in the API. Specially electron doesn't have all of the chrome.* API which is needed for chromium browser to work properly, many flags still doesn't have proper replacements such as the headless flag.

Below you will see two ways to do it. However you need to make sure of two points,

  • Make sure the puppeteer is connected before the app is initiated.
  • Make sure you get the correct version puppeteer or puppeteer-core for the version of Chrome that is running in Electron!

Use puppeteer-in-electron

There are lots of workarounds, but most recently there is a puppeteer-in-electron package which allows you to run puppeteer within electron app using the electron.

First, install the dependencies,

npm install puppeteer-in-electron puppeteer-core electron

Then run it.

import {BrowserWindow, app} from "electron";
import pie from "puppeteer-in-electron";
import puppeteer from "puppeteer-core";

const main = async () => {
  const browser = await pie.connect(app, puppeteer);

  const window = new BrowserWindow();
  const url = "https://example.com/";
  await window.loadURL(url);

  const page = await pie.getPage(browser, window);
  console.log(page.url());
  window.destroy();
};

main();

Get the debugging port and connect to it

The another way is to get the remote-debugging-port of the electron app and connect to it. This solution is shared by trusktr on electron forum.

import {app, BrowserWindow, ...} from "electron"
import fetch from 'node-fetch'

import * as puppeteer from 'puppeteer'

app.commandLine.appendSwitch('remote-debugging-port', '8315')

async function test() {
    const response = await fetch(`http://localhost:8315/json/versions/list?t=${Math.random()}`)
    const debugEndpoints = await response.json()

    let webSocketDebuggerUrl = debugEndpoints['webSocketDebuggerUrl ']

    const browser = await puppeteer.connect({
        browserWSEndpoint: webSocketDebuggerUrl
    })

    // use puppeteer APIs now!
}

// ... make your window, etc, the usual, and then: ...

  // wait for the window to open/load, then connect Puppeteer to it:
  mainWindow.webContents.on("did-finish-load", () => { 
    test()
  })

Both solution above uses webSocketDebuggerUrl to resolve the issue.

Extra

Adding this note because most people uses electron to bundle the app.

If you want to build the puppeteer-core and puppeteer-in-electron, you need to use hazardous and electron-builder to make sure get-port-cli works.

Add hazardous on top of main.js

// main.js
require ('hazardous');

Make sure the get-port-cli script is unpacked, add the following on package.json

"build": {
  "asarUnpack": "node_modules/get-port-cli"
}

Result after building:

like image 111
Md. Abu Taher Avatar answered Sep 28 '22 10:09

Md. Abu Taher