配置Electron中Puppeteer executablePath的正确姿势
TL; NR
- 轻量, remote调用(使用本地chrome) =>
puppeteer-core
- 将chromium整合进安装包(需要asar打包时排除chromium), macOS下验证失败 =>
puppeteer
2020.03.18更新
现在要在electron中使用puppeteer的话可以使用puppeteer-in-electron这个工具,让puppeteer连接到electron启动的chromium实例。代码示例:
import {BrowserWindow, app} from "electron";
import pie from "puppeteer-in-electron";
import puppeteer from "puppeteer-core";
const main = async () => {
// 初始化,开启的远程调试功能
pie.initialize(app);
// 返回一个puppeteer browser
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();
核心源码:
// electron开启远程调试端口
app.commandLine.appendSwitch(
"remote-debugging-port",
`${port}`
);
// puppeteer-core通过ws地址连接到chromium实例
const port = app.commandLine.getSwitchValue("remote-debugging-port");
const response = await retry(() => fetch(`http://127.0.0.1:${port}/json/version`));
const json = await response.json();
const browser = await puppeteer.connect({
browserWSEndpoint: json.webSocketDebuggerUrl,
defaultViewport: null
});
}
正文
这个问题在我想在electron应用中使用puppeteer爬虫时出现,具体问题是如何引用正确的chromium/chrome位置,尤其是在打包后的环境下。
在开始之前,首先需要的是配置puppeteer.launch()
函数启动时的executablePath
参数,一般情况下使用puppeteer.executablePath()
获取默认引用的可执行chrome位置即可:
import puppeteer from 'puppeteer'
const PresetPage = async() => {
const browser = await puppeteer.launch(Object.assign(BrowserConfig.LaunchOption, {
executablePath: puppeteer.executablePath()
}))
...
}
其中:
console.log(puppeteer.executablePath())
// /Users/yrq/Desktop/project/sprite-garden/node_modules/puppeteer/.local-chromium/mac-599821/chrome-mac/Chromium.app/Contents/MacOS/Chromium
可以看到会使用安装在本地依赖中的chromium。在开发环境下是没有问题,但在electron应用打包后无法获取到chromium的正确路径导致puppeteer无法运行。
为了解决这个问题,尝试使用下面两种方法。
使用puppeteer-core远程调用chrome
已验证
在loukaspd/puppeteer-electron-quickstart中发现的方法。
puppeteer
在1.7.0
版本后发布了puppeteer-core
,可看做是轻量版的puppeteer
,可以用它远程调用一个已安装的浏览器。
yarn add puppeteer-core
它们之间的不同:
puppeteer-core
在安装时不会自动下载Chromium
puppeteer-core
会忽略所有PUPPETEER_*
环境变量
import puppeteer from 'puppeteer-core'
const PresetPage = async() => {
const browser = await puppeteer.launch(Object.assign(BrowserConfig.LaunchOption, {
executablePath: this.chromePath
}))
...
}
关于chromePath
的值从下面的方法中获得: 配置文件
+ 默认安装路径
-
在设置文件中寻找chrome路径
... getSavedPath() { const settingsPath = this._filePaths.settingsPath(); return new Promise((resolve, reject) => { if (!fs.existsSync(settingsPath)) { resolve(undefined); return; } fs.readFile(settingsPath, "utf8", (err, fileContent) => { if (err) { console.log(err) reject(); return; } resolve(fileContent); }); }) } settingsPath() { return path.join(this.appFolderPath(), 'settings.json'); } appFolderPath() { const documentsPath = path.join(os.homedir(), "Documents"); return path.join(documentsPath, this.appFolderName); } ...
-
在默认安装路径寻找chrome
根据系统类型返回结果:
const getDefaultOsPath = () => { if (process.platform === 'win32') { return 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe' } else { return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' } } this.chromePath = getDefaultOsPath()
若在两种途径中均未找到,则初始化会失败。
使用内置的chromium
macOS下验证失败
,可跳过
-
转换
puppeteer.executablePath()
得到的路径使用正则替换chromium的路径:
function getChromiumExecPath() { return puppeteer.executablePath().replace('app.asar', 'app.asar.unpacked'); } export function createBrowser(options = {}) { return puppeteer.launch({ ...options, executablePath: getChromiumExecPath() }); }
-
修改
package.json
中的构建配置不将puppeteer下载的chromium打包进asar包中:
... "build": { "asar": true, "asarUnpack": "node_modules/puppeteer/.local-chromium/**/*", ... } ...
这种方法在我的项目中(macOS系统)没有成功,在puppeteer.launch()
时报了如下错误:
Failed to launch chrome!
dlopen /Users/yrq/Desktop/project/sprite-garden/build/mac/sprite-garden.app/Contents/Resources/app.asar.unpacked/node_modules/puppeteer/.local-chromium/mac-609904/chrome-mac/Chromium.app/Contents/MacOS/../Versions/72.0.3617.0/Chromium Framework.framework/Chromium Framework: dlopen(/Users/yrq/Desktop/project/sprite-garden/build/mac/sprite-garden.app/Contents/Resources/app.asar.unpacked/node_modules/puppeteer/.local-chromium/mac-609904/chrome-mac/Chromium.app/Contents/MacOS/../Versions/72.0.3617.0/Chromium Framework.framework/Chromium Framework, 261): image not found
TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md
类似的错误源于系统的差异性,目前无法解决
参考
- 原文作者:yrq110
- 原文链接:http://yrq110.me/post/front-end/the-right-way-to-use-puppeteer-in-electron/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。