如何将木偶芯与电子一起使用?

时间:2019-10-03 06:14:47

标签: javascript typescript electron puppeteer

我从另一个Stackoverflow问题获得了此代码:

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编译器抱怨executablePath方法选项对象的launch属性,因为它必须是string类型,而不是Electron类型。那么如何将电子铬可执行路径传递给操纵up呢?

3 个答案:

答案 0 :(得分:2)

没有一些变通办法和标志更改,您不能直接将电子可执行文件与Puppeteer一起使用。他们在API方面有很多差异。特别是电子不具有所有的 chrome。* API,这是Chrome浏览器正常工作所需的API,许多标志仍然没有适当的替代物,例如the headless flag

下面您将看到两种方法。但是,您需要确保两点,

  • 在启动应用程序之前,确保木偶已连接。
  • 确保为在Electron中运行的Chrome版本获得正确的puppeteer或puppeteer-core版本!

使用puppeteer-in-electron

有很多变通办法,但是最近有一个puppeteer-in-electron软件包,可让您使用电子在电子应用程序中运行木偶。

首先,安装依赖项,

npm install puppeteer-in-electron puppeteer-core electron

然后运行它。

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();

获取调试端口并连接到它

另一种方法是获取电子应用程序的远程调试端口并连接到它。 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()
  })

以上两种解决方案均使用 webSocketDebuggerUrl 解决此问题。

额外

添加此注释是因为大多数人使用电子来捆绑应用程序。

如果要构建木偶内核和电子木偶,则需要使用hazardouselectron-builder来确保get-port-cli有效。

在main.js顶部添加危险

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

确保已解压缩get-port-cli脚本,在package.json上添加以下内容

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

构建后的结果

答案 1 :(得分:0)

还有另一个选项,适用于电子5.x.y及更高版本(目前适用于电子7.x.y,我尚未在8.x.y beta中对其进行测试):

// const assert = require("assert");
const electron = require("electron");
const kill = require("tree-kill");
const puppeteer = require("puppeteer-core");
const { spawn } = require("child_process");

let pid;

const run = async () => {
  const port = 9200; // Debugging port
  const startTime = Date.now();
  const timeout = 20000; // Timeout in miliseconds
  let app;

  // Start Electron with custom debugging port
  pid = spawn(electron, [".", `--remote-debugging-port=${port}`], {
    shell: true
  }).pid;

  // Wait for Puppeteer to connect
  while (!app) {
    try {
      app = await puppeteer.connect({
        browserURL: `http://localhost:${port}`,
        defaultViewport: { width: 1000, height: 600 } // Optional I think
      });
    } catch (error) {
      if (Date.now() > startTime + timeout) {
        throw error;
      }
    }
  }

  // Do something, e.g.:
  // const [page] = await app.pages();
  // await page.waitForSelector("#someid")// 
  // const text = await page.$eval("#someid", element => element.innerText);
  // assert(text === "Your expected text");
  // await page.close();
};

run()
  .then(() => {
    // Do something
  })
  .catch(error => {
    // Do something
    kill(pid, () => {
      process.exit(1);
    });
  });

获取pid和使用kill是可选的。对于在某些CI平台上运行脚本没有关系,但是对于本地环境,每次尝试失败后,您都必须手动关闭电子应用。

请参阅此sample repo

答案 2 :(得分:0)

最好的答案对我来说不起作用,使用电子 11 和 puppeteer-core 8。 但是在主进程中启动 puppeteer 然后在渲染器进程中对我有用。您可以使用 ipcMain 和 ipcRenderer 相互通信。下面的代码

main.ts(主进程代码)

import { app, BrowserWindow, ipcMain } from 'electron';
import puppeteer from 'puppeteer-core';
async function newGrabBrowser({ url }) {
  const browser = await puppeteer.launch({
    headless: false,
    executablePath:
      '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  });
  const page = await browser.newPage();
  page.goto(url);
}
ipcMain.on('grab', (event, props) => {
  newGrabBrowser(JSON.parse(props));
});

home.ts(渲染进程代码)

const { ipcRenderer } = require('electron');
ipcRenderer.send('grab',JSON.stringify({url: 'https://www.google.com'}));