TL; NR
- 轻量, remote调用(使用本地chrome) =>
puppeteer-core
- 将chromium整合进安装包(需要asar打包时排除chromium), macOS下验证失败 =>
puppeteer
这个问题在我想在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
类似的错误源于系统的差异性,目前无法解决