electron 电子+ puppet 师页面,2等待选择器:传递的函数不能很好地序列化

q5iwbnjs  于 2022-12-08  发布在  Electron
关注(0)|答案(1)|浏览(206)

我试图使用puppeteer与electron.js,但没有功能相关的等待和点击做工作。
它总是给出:

Error: Passed function is not well-serializable!
    at ExecutionContext._ExecutionContext_evaluate

示例函数:

// url: https://example.com/
async function scraper(url) {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto(url);
    const element = await page.waitForSelector("h1");
    console.log('element: ', element)
    await browser.close();
}

这些线程中提到的解决方案都不起作用。(用evals / rewriting wait函数 Package )Puppeteer in Electron: Error: Passed function is not well-serializable
Uncaught (in promise) Error: Passed function is not well-serializable
有什么想法吗?

qacovj5a

qacovj5a1#

好的,我解决了这个问题。首先,上下文:一般来说,正如在注解中所写的,你不能在浏览器环境中运行puppeteer,它只能在odejs中运行。电子提供了2个进程renderer和main。无论何时你想使用节点,你都必须在main中运行。关于这两个进程之间的通信,你可以在docs中阅读,有很多方法可以处理它。2据我所知,最好的做法是在preload中声明它并使用ipc桥。3其他的解决方案违反了contextIsolation规则。
我从一个问题徘徊到另一个问题:比如不可序列化的函数,不需要定义,还有很多其他的。最后,我从头开始重写了所有的东西,它工作了,下面是我的解决方案:
main.js

const { app, BrowserWindow } = require('electron')
const path = require('path')
const { ipcMain } = require('electron');
const puppeteer = require("puppeteer");

function createWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true,
      contextIsolation: true,
    },
  })
  ipcMain.handle('ping', async () => {
    await checkPup()
  })

  async function checkPup() {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://example.com');
    page
      .waitForSelector('h1', { visible: true })
      .then(() => {
        console.log('got it')
      });
    const [button] = await page.$x("//button[contains(., 'Create account')]");
    if (button) {
      console.log('button: ', button)
      await button.click();
      await page.screenshot({ path: 'tinder.png' });

      const [button] = await page.$x("//button[contains(., 'Create account')]");
      if (button) {
        console.log('button: ', button)
        await button.click();
        await page.screenshot({ path: 'tinder.png' });
      }
    }
    await browser.close();
  }
  // and load the index.html of the app.
  mainWindow.loadFile('index.html')
  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow()

  app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

// Attach listener in the main process with the given ID
ipcMain.on('request-mainprocess-action', (event, arg) => {
  // Displays the object sent from the renderer process:
  //{
  //    message: "Hi",
  //    someData: "Let's go"
  //}
  console.log(
    arg
  );
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

preload.js

// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
const { contextBridge, ipcRenderer } = require('electron')
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const type of ['chrome', 'node', 'electron']) {
    replaceText(`${type}-version`, process.versions[type])
  }
})

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  ping: () => ipcRenderer.invoke('ping'),
  // we can also expose variables, not just functions
})

renderer.js

const information = document.getElementById('info')
const btn = document.getElementById('btn')
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`

btn.addEventListener('click', () => {
    console.log('habad!!!!!')
    func()
})

const func = async () => {
    const response = await window.versions.ping()
    information.innerText = response;
    console.log(response) // prints out 'pong'
  }

对不起,有点混乱,我希望它会帮助某人,也许找到解决其他一些问题的办法

相关问题