Puppeteer Browser.version() 解析:获取浏览器名称与版本号的 API 用法与源码实现
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
Browser.version()是 Puppeteer 中用于获取当前已连接浏览器的名称和版本号字符串的实例方法,返回形如"Chrome/61.0.3153.0"的Promise<string>。它在自动化测试中用于环境检测、浏览器兼容性判断、版本门控等功能,是理解 Puppeteer 底层 CDP / WebDriver BiDi 协议差异的关键入口。
方法签名与返回值
Browser.version()在 api/Browser.ts 中作为抽象方法声明:
class Browser { abstract version(): Promise<string>; }方法无参数,返回一个Promise<string>,resolve 值为浏览器的名称加版本号字符串。根据运行环境和浏览器类型,返回值的格式存在差异:
| 运行环境 | 返回值示例 | 说明 |
|---|---|---|
| 无头 Chrome(旧 headless) | "HeadlessChrome/61.0.3153.0" | 名称前缀为HeadlessChrome |
| 有头 Chrome 或新 headless 模式 | "Chrome/61.0.3153.0" | 名称前缀为Chrome |
| Firefox | "Firefox/116.0a1" | 名称前缀为Firefox |
官方文档明确提示:返回值的格式可能随浏览器的未来版本发布而变化。因此,生产代码中不应将返回值与某个固定字符串做严格相等比较,而应通过解析前缀或版本号区间来判断特性。
CDP 协议的实现:调用Browser.getVersion
Chrome/Chromium 浏览器通过 Chrome DevTools Protocol(CDP)通信。在 cdp/Browser.ts 中,version()方法的具体实现如下:
// packages/puppeteer-core/src/cdp/Browser.ts override async version(): Promise<string> { const version = await this.#getVersion(); return version.product; }它调用了私有方法#getVersion()(第 713-725 行),该方法内部通过Connection.send发送 CDP 命令Browser.getVersion:
async #getVersion(): Promise<Protocol.Browser.GetVersionResponse> { if (!this.#version) { this.#version = Deferred.create<Protocol.Browser.GetVersionResponse>(); try { this.#version.resolve( await this.#connection.send('Browser.getVersion'), ); } catch (error) { this.#version.reject(error as Error); } } return await this.#version.valueOrThrow(); }这段实现有两个关键设计点:
- 结果缓存:
#version是一个Deferred实例,首次调用时发起 CDP 请求,之后所有调用都复用缓存的 Promise,避免重复向浏览器端发送Browser.getVersion命令。 - 错误传播:若 CDP 请求失败(例如连接已断开),
Deferred.reject会将错误传播给所有等待该 Promise 的调用方。
version()方法返回的是GetVersionResponse中的product字段。在旧版 headless 模式下,该字段值为"HeadlessChrome/xx.x.x.x";在新 headless(Chrome 112+ 默认)或有头模式下,则为"Chrome/xx.x.x.x"。这与userAgent()方法(第 685-688 行)返回userAgent字段的逻辑形成对照——两者共用同一个#getVersion()缓存,但提取的字段不同。
WebDriver BiDi 协议的实现
对于通过 WebDriver BiDi 协议连接的浏览器(如 Firefox 的 BiDi 模式),bidi/Browser.ts 中的实现直接拼接内部字段:
// packages/puppeteer-core/src/bidi/Browser.ts override async version(): Promise<string> { return `${this.#browserName}/${this.#browserVersion}`; }#browserName和#browserVersion在 BiDi 连接建立时,从session.new的 capabilities 响应中提取。这种实现不依赖额外的协议命令调用,而是在会话初始化阶段就已经拿到了版本信息,因此version()的调用几乎是零开销的。
实际用法示例
以下代码演示了如何启动浏览器并获取版本号:
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const version = await browser.version(); console.log(version); // 例如: "Chrome/131.0.6778.204" 或 "HeadlessChrome/131.0.6778.204" // 版本门控示例:判断是否为 Chrome 149+ const majorVersion = parseInt(version.match(/\d+/)?.[0] ?? '0', 10); if (majorVersion < 149) { console.warn('某些功能需要 Chrome 149 或更高版本'); } // 判断是否为 headless 模式 const isHeadless = version.startsWith('HeadlessChrome'); console.log(`当前运行模式: ${isHeadless ? '无头' : '有头'}`); await browser.close();上述版本门控模式在 Puppeteer 源码中本身就有应用。在 cdp/Browser.ts 的create静态方法中,当用户传入allowlist选项时,会主动调用#getVersion()解析主版本号并检查是否满足最低要求:
if (allowlist) { const version = await browser.#getVersion(); const majorVersion = parseInt( version.product.match(/\d+/)?.[0] ?? '0', 10, ); if (majorVersion < 149) { throw new Error('The allowlist option require Chrome 149 or greater.'); } }这说明version()的返回值在 Puppeteer 内部不仅用于用户侧检测,也是实现特性门控的基础设施。
与userAgent()方法的区别
| 方法 | 返回内容 | 示例(Chrome) | 示例(Firefox) |
|---|---|---|---|
browser.version() | 名称/版本号 | "Chrome/131.0.6778.204" | "Firefox/134.0" |
browser.userAgent() | 完整 User-Agent 字符串 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" | "Mozilla/5.0 (X11; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0" |
version()返回的是简洁的标识字符串,适合用于版本比较和程序逻辑判断;userAgent()返回的是完整的 HTTP User-Agent,适合用于需要精确匹配 UA 的场景(如反爬检测模拟)。两者在 CDP 实现中共用同一个Browser.getVersion命令的结果缓存(第 680-688 行),不会产生额外的协议调用开销。
测试验证
Puppeteer 仓库中的 test/src/browser.test.ts 包含了针对Browser.version()的专项测试:
describe('Browser.version', function () { it('should return version', async () => { const {browser} = await getTestState(); const version = await browser.version(); expect(version.length).toBeGreaterThan(0); expect(version.toLowerCase()).atLeastOneToContain(['firefox', 'chrome']); }); });该测试验证了两个核心不变量:
- 返回值非空;
- 返回值(不区分大小写)必须包含
firefox或chrome字样。
这确保了无论底层是 Chrome(CDP 或 BiDi)还是 Firefox,version()的返回值都遵循名称/版本的基本格式约定。
注意事项与最佳实践
- 不要在
browser.close()之后调用:CDP 实现依赖活动连接,连接断开后调用将抛出错误。BiDi 实现不受此影响,因为它从会话初始化时就缓存了版本信息。 - 不要依赖固定格式:文档明确声明返回值格式可能随浏览器版本变化。建议通过正则提取数字部分做版本比较,而非字符串全等匹配。
- 区分 headless 与有头模式:旧版 headless Chrome 返回
HeadlessChrome/...前缀,新 headless 和有头模式返回Chrome/...前缀。如果需要判断运行模式,应使用version.startsWith('Headless')而非!version.startsWith('Chrome')。 - 版本缓存的语义:CDP 实现中版本信息在首次
version()调用时被缓存,之后不会更新。由于浏览器进程在运行期间版本号不会变化,这是合理的设计。
相关文档
- Browser 类完整 API
- Browser.userAgent() 方法
- Browser 类源码(抽象定义)
- CDP Browser 实现
- BiDi Browser 实现
- Browser 测试用例
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考