1. 项目概述与核心价值
如果你正在寻找一种能够绕过官方API限制,直接、稳定地自动化操作Google Gemini网页版的方法,那么gemini-skill这个项目绝对值得你花时间深入研究。它不是一个简单的脚本,而是一个基于Chrome DevTools Protocol(CDP)和MCP(Model Context Protocol)协议构建的、具备完整工程化思维的自动化解决方案。简单来说,它通过程序化地控制一个真实的浏览器,模拟人类在gemini.google.com上的所有操作,从而实现了AI生图、多轮对话、图片上传与提取等一系列功能。
这个项目的核心价值在于其“直连”特性。它不依赖任何可能随时变更、有调用配额或地域限制的官方API,而是直接与Gemini的Web服务交互。这意味着,只要你能用浏览器正常访问Gemini,这个工具就能工作。对于需要批量生成图片、自动化内容创作、或者将Gemini能力深度集成到自己工作流中的开发者和创作者来说,这提供了一种非常可靠且可控的途径。我花了几天时间深入测试了它的每一个功能模块,从环境搭建到实际生图,踩过一些坑,也总结了不少经验。接下来,我将为你完整拆解这个项目的设计思路、实操细节以及那些在官方文档里不会写的避坑指南。
2. 核心架构与设计哲学解析
2.1 为什么选择CDP而非传统爬虫?
在自动化网页操作领域,常见的选择有Selenium、Playwright和Puppeteer(底层也是CDP)。gemini-skill选择了最底层的CDP,这是一个非常关键且明智的设计决策。CDP是Chrome/Chromium浏览器原生提供的调试协议,它允许外部程序通过WebSocket与浏览器实例进行通信,发送指令来操作DOM、执行JavaScript、拦截网络请求等。
选择CDP的核心原因在于稳定性和反检测能力。像Gemini这样的大型AI服务,其前端必然部署了复杂的反自动化检测机制,用于识别Selenium或Playwright这类自动化工具留下的特征(如特定的navigator.webdriver属性)。虽然Playwright和Puppeteer-extra也提供了stealth插件,但直接使用CDP配合puppeteer-extra-plugin-stealth,可以让我们在最底层进行指纹伪装,模拟出与真人操作几乎无异的浏览器环境,极大降低了被风控系统识别和封锁的风险。项目中的browser.js和daemon/engine.js模块就负责建立和维护这个“隐身”的CDP连接。
2.2 Daemon模式:持久化与资源管理的智慧
项目最精妙的设计之一是引入了Browser Daemon(守护进程)。传统的自动化脚本通常是“一次性”的:启动浏览器 -> 执行任务 -> 关闭浏览器。对于需要频繁调用Gemini的场景,反复启动浏览器(一个非常耗时的过程)是无法接受的。
Daemon模式解决了这个问题。它作为一个独立的HTTP后台服务(daemon/server.js)运行,负责管理浏览器实例的生命周期。其工作流程如下:
- 按需启动:当第一个MCP工具被调用时,如果Daemon未运行,系统会自动在后台启动它。
- 连接复用:Daemon启动后,会拉起一个浏览器进程并开启CDP调试端口。后续所有的操作请求都通过HTTP API(
/browser/acquire)连接到这个已有的浏览器实例,避免了重复启动。 - 惰性销毁:浏览器实例不会永远驻留。Daemon内置了一个30分钟(可配置)的倒计时器。每次有操作请求时,计时器重置。如果连续30分钟没有任何请求,Daemon会自动关闭浏览器并退出自身进程,释放系统资源。
- 状态保持:由于浏览器用户数据目录(
userDataDir)被持久化,登录状态、Cookies、本地存储等得以保留。这意味着你只需要在首次运行时手动登录一次Google账号,之后的所有自动化会话都是已登录状态。
这种设计完美平衡了“响应速度”和“资源占用”。对于高频但间歇性的自动化任务(比如每隔几分钟生成一张图),体验非常流畅。
2.3 MCP协议:标准化与生态集成
MCP(Model Context Protocol)是由Anthropic提出的一种开放协议,旨在标准化AI模型与外部工具、数据源之间的通信方式。gemini-skill将自己包装成一个MCP Server,这带来了巨大的生态优势。
这意味着什么?任何支持MCP协议的AI客户端(如Claude Desktop、Cursor、Windsurf、CodeBuddy等)都可以直接发现并调用gemini-skill提供的工具,而无需为每个客户端单独编写集成代码。AI助手可以直接在对话中告诉你:“我可以帮你用Gemini生成一张图”,然后调用gemini_generate_image工具,并将生成的图片展示给你。这实现了AI能力的“即插即用”。
项目的src/mcp-server.js就是这个MCP Server的实现,它按照MCP的JSON-RPC over stdio规范,注册了前文表格中列出的所有工具(gemini_generate_image,gemini_new_chat等)。当AI客户端需要生图时,它会通过标准输入输出(stdio)向这个Server发送一个格式化的JSON请求,Server执行操作后,再将结果通过JSON返回。
3. 环境准备与首次配置实战
3.1 系统与软件准备
开始之前,请确保你的环境满足以下要求:
- Node.js:版本必须大于等于18。我推荐使用
nvm(Node Version Manager)来管理Node版本,可以轻松切换。在终端输入node -v确认版本。 - 浏览器:系统中需要安装Chrome、Edge或Chromium其中一种。项目会自动检测。务必确保你能用该浏览器正常访问并登录
https://gemini.google.com。这是后续一切自动化操作的前提。 - Git:用于克隆项目仓库。
3.2 项目初始化与依赖安装
打开终端,执行以下命令获取项目代码并安装依赖:
git clone https://github.com/WJZ-P/gemini-skill.git cd gemini-skill npm install这个过程会安装puppeteer-extra、puppeteer-extra-plugin-stealth、sharp(用于图片处理)、ws(WebSocket)等核心依赖。如果npm install速度慢,可以考虑配置淘宝镜像或使用pnpm。
3.3 关键配置详解与避坑指南
项目根目录下有一个.env.example文件,你需要将其复制一份并重命名为.env,然后根据你的情况进行修改。这个文件里的配置至关重要,直接决定了工具能否正常运行。
cp .env.example .env # 然后用文本编辑器打开 .env 文件进行编辑下面我逐一解释几个最关键的配置项,并分享我的配置心得:
1.BROWSER_PATH(浏览器路径)
- 默认行为:不设置此变量,项目会按照
Chrome->Edge->Chromium的顺序自动检测系统已安装的浏览器。 - 手动指定:如果你安装了多个浏览器,或者浏览器不在标准位置,可以手动指定。例如:
# Windows 示例 BROWSER_PATH=C:\Program Files\Google\Chrome\Application\chrome.exe # macOS 示例 BROWSER_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome - 避坑提示:在Windows Server或某些Linux服务器上,自动检测可能会失败。如果遇到
No usable browser found错误,手动指定绝对路径是最可靠的解决方案。
2.BROWSER_HEADLESS(无头模式)
- 首次运行,务必设为
false:
无头模式意味着浏览器在后台运行,没有图形界面。首次运行时,你需要手动完成Google账号登录。如果开启无头模式,你将看不到登录页面,导致自动化卡住。首次登录成功后,登录信息会保存在BROWSER_HEADLESS=falseuserDataDir,后续你可以将其改为true以提升性能并节省资源。 - 生产环境建议:稳定运行后,可以改为
true,这样浏览器不会弹出窗口,更适合服务器环境。
3.BROWSER_USER_DATA_DIR(用户数据目录)
- 默认行为:不设置时,项目会尝试使用
~/.wjz_browser_data目录,如果该目录不存在,则会回退到浏览器默认的用户数据目录中的一个子目录(例如~/.config/google-chrome/Profile 1)。 - 自定义目录:你可以指定一个固定的目录来保存浏览器数据,这样即使项目更新或重装,登录状态也不会丢失。
BROWSER_USER_DATA_DIR=/path/to/your/gemini_profile - 重要提醒:确保你指定的目录有读写权限。在Linux/Mac上,注意目录所有权;在Windows上,避免使用需要管理员权限的路径。
4.BROWSER_DEBUG_PORT(CDP调试端口)
- 默认值:
40821。这是Daemon启动浏览器时开启远程调试的端口号。 - 端口冲突:确保这个端口没有被其他程序占用。如果你同时运行多个基于CDP的自动化项目,需要为它们分配不同的端口。
- 关于OpenClaw:如果你也在使用OpenClaw,它的默认CDP端口是
18800。你可以通过设置BROWSER_DEBUG_PORT=18800来尝试复用OpenClaw的浏览器实例。但请注意:这样做会失去本项目内置的Stealth反爬保护,因为OpenClaw的浏览器实例可能没有加载反检测插件。在稳定性上可能有风险,仅建议在开发调试时尝试。
5.DAEMON_TTL_MS(守护进程闲置超时)
- 默认值:
1800000毫秒,即30分钟。 - 调整建议:如果你的任务间隔可能超过30分钟,但又希望保持会话,可以适当调大这个值,比如设置为
3600000(1小时)。但请勿设置过大,以免浏览器进程长期占用内存。
4. 核心功能实操与代码解析
4.1 启动与连接:从MCP调用到浏览器会话
让我们跟踪一次完整的gemini_generate_image工具调用,看看数据是如何流动的。
第一步:MCP客户端发起请求假设你在Claude Desktop中要求生成一张“星空下的城堡”图片。Claude(作为MCP Client)会通过配置好的命令启动src/mcp-server.js,并通过stdio发送一个JSON-RPC请求:
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "gemini_generate_image", "arguments": { "prompt": "A majestic castle under a starry night sky, digital art, highly detailed", "fullSize": true } } }第二步:MCP Server处理与Daemon交互mcp-server.js收到请求后,会调用对应的工具函数。这个函数位于gemini-ops.js中。工具函数的第一件事就是通过browser.js模块获取一个可用的浏览器CDP连接。
browser.js的ensureBrowser()函数是连接中枢:
- 它首先检查本地是否已有可用的CDP WebSocket连接,如果有则直接使用。
- 如果没有,它会向Daemon的HTTP服务(
http://localhost:40225)发送GET /browser/acquire请求。 - Daemon收到
acquire请求后,会检查自己管理的浏览器实例是否存活。如果不存在,则调用engine.js启动一个新的浏览器进程(应用Stealth插件,加载用户数据目录)。如果已存在,则直接返回其CDP WebSocket连接地址(ws://localhost:40821/devtools/browser/...)。 browser.js拿到WebSocket地址后,使用ws库建立连接,并封装成一套简单的CDP命令发送接口。
至此,一个从AI对话到真实浏览器页面的桥梁就搭建完成了。
4.2 AI生图全流程拆解
获取到浏览器连接后,gemini-ops.js中的generateImage函数开始执行其精密操作。这个过程模拟了人类用户的所有操作:
- 页面导航与就绪检查:首先,它确保浏览器当前标签页位于Gemini的聊天页面(
https://gemini.google.com/)。它会通过CDP执行JavaScript代码,探测页面中关键元素(如输入框、发送按钮)是否存在,确保页面完全加载且处于可交互状态。 - 输入Prompt:通过CDP的
Input.dispatchKeyEvent和DOM.setAttributeValue等方法,将生图的Prompt文本填入输入框。这里有一个细节:Gemini的输入框可能是一个contenteditable的div,而不是简单的<input>,所以操作逻辑需要适配。 - 触发生成:模拟点击“发送”按钮(或按下回车键)。这一步之后,Gemini开始处理请求。
- 等待与轮询:这是最耗时的部分。函数会进入一个循环,定期(例如每秒)检查页面DOM,寻找代表生成进度的元素(如“正在生成图像...”的提示文本),或者最终生成的图片容器。
- 超时处理:工具内置了超时机制(默认2分钟)。如果超时未检测到结果,会抛出错误。这也是为什么在MCP客户端配置中建议将
timeoutMs设得足够长(如3分钟)。
- 超时处理:工具内置了超时机制(默认2分钟)。如果超时未检测到结果,会抛出错误。这也是为什么在MCP客户端配置中建议将
- 提取图片信息:一旦检测到图片生成完成,函数会定位到图片元素,获取其
src属性。这通常是一个Google内部的CDN URL或者一个Blob URL。 - 下载与保存:
- 如果
fullSize参数为true,工具会采取更复杂的方式获取原图。它可能会模拟点击图片,打开预览模态框,然后从模态框中提取更高分辨率的图片URL,再通过CDP的Fetch域拦截并下载图片数据。 - 如果
fullSize为false或获取原图失败,则直接下载DOM中展示的图片。 - 下载的图片数据(通常是二进制Buffer)会传递给
watermark-remover.js模块。
- 如果
- 智能去水印:
watermark-remover.js使用sharp库处理图片。Gemini生成图片的右下角通常有一个半透明的“Gemini”文字水印。这个模块的策略不是简单的裁剪,而是通过分析图片边缘像素,定位水印区域,然后尝试用周围背景的颜色进行智能填充或修复,尽可能无损地移除水印。处理后的图片最终保存到OUTPUT_DIR配置的目录中。 - 返回结果:工具函数将图片的本地文件路径、元数据等信息封装成JSON,通过MCP协议返回给AI客户端。Claude Desktop就能在对话中显示“图片已生成,保存于:xxx”的消息,甚至可能直接预览图片。
4.3 图片上传与“图生图”实现
gemini_upload_images工具实现了上传本地图片作为参考图的功能。其原理是:
- 文件选择框模拟:通过CDP的
Page.setFileInputFiles方法,可以直接将本地文件路径“注入”到网页的文件选择<input>元素中,而无需手动触发文件选择对话框。这是浏览器自动化中处理文件上传的标准且最可靠的方式。 - 等待上传完成:上传后,页面通常会有加载指示器。工具会等待直到图片缩略图出现在输入框附近,表示上传成功。
- 组合Prompt:上传完成后,你可以在后续的
gemini_generate_image调用中,将文字Prompt与已上传的参考图结合,实现“以图生图”的效果。操作层会自动处理图片与文本的上下文关联。
4.4 会话管理技巧
项目提供了灵活的会话管理工具,这对于复杂的自动化流程非常有用。
gemini_new_chat:点击Gemini界面上的“新聊天”按钮,开启一个全新的、独立上下文会话。适合开始一个全新的、不相关的任务。gemini_temp_chat:进入“临时对话”模式。在这个模式下进行的对话不会保存到聊天历史记录中,适合执行一些一次性的、不希望留下痕迹的查询或生成任务。gemini_navigate_to:这是一个强大的功能。你可以将某个历史会话的完整URL(从浏览器地址栏复制)作为参数传入,工具会直接导航到那个特定的会话页面。这意味着你可以自动化处理历史对话,比如对昨天生成的图片进行批量下载,或者继续某个未完成的创作。
5. 常见问题排查与实战心得
在实际部署和使用gemini-skill的过程中,我遇到并解决了一些典型问题。这里分享给你,希望能帮你节省时间。
5.1 登录失败或会话丢失
- 症状:工具运行后,浏览器打开了,但一直停留在Gemini首页,没有自动进入已登录的聊天界面,或者提示需要重新登录。
- 排查步骤:
- 检查
BROWSER_HEADLESS:首次运行必须为false,确保你能看到浏览器并手动完成登录。 - 检查
BROWSER_USER_DATA_DIR:确认配置的目录存在且有读写权限。登录成功后,你可以检查该目录下是否生成了Default(或类似)的文件夹,里面应有Cookies、Local Storage等文件。 - 清除旧的用户数据:有时旧的用户数据可能损坏。可以尝试备份后,删除整个用户数据目录,然后重新运行工具并登录。
- Google账号安全设置:确保你的Google账号没有开启二次验证(2FA)中过于严格的设置,或者尝试在常用设备/IP上先手动登录一次Gemini。
- 检查
5.2 生图超时或失败
- 症状:
gemini_generate_image工具长时间无响应,最终返回超时错误。 - 排查与解决:
- 增加超时时间:首先,确保MCP客户端(如Claude Desktop)和工具本身的超时设置足够长。在
.env中,可以适当增加BROWSER_PROTOCOL_TIMEOUT。在调用工具时,传入timeout参数(单位秒)。 - 检查网络与Gemini服务:Gemini生图服务本身可能不稳定或较慢。手动访问Gemini网页版,尝试生成同一张图,看是否同样缓慢或失败。
- 检查页面元素选择器:Gemini的网页UI可能会更新,导致工具中用于定位“发送按钮”、“生成状态”、“图片容器”的CSS选择器失效。你需要打开浏览器的开发者工具(F12),检查当前页面的DOM结构,并与
src/operator.js或src/gemini-ops.js中的选择器常量进行比对。这是开源项目需要持续维护的部分。 - 启用可视化调试:将
BROWSER_HEADLESS设为false,观察自动化过程的每一步。看它是否成功输入了文字,是否点击了按钮,是否卡在某个加载状态。这能最直观地定位问题。
- 增加超时时间:首先,确保MCP客户端(如Claude Desktop)和工具本身的超时设置足够长。在
5.3 Daemon进程异常退出
- 症状:第一次调用成功,但间隔一段时间后再次调用失败,提示无法连接到Daemon。
- 排查:
- 检查日志:Daemon进程的输出在终端或日志文件中。查看是否有错误信息,比如端口被占用、浏览器崩溃等。
- 检查TTL设置:确认
DAEMON_TTL_MS的设置是否符合预期。如果设置过短,可能在你想用的时候浏览器已经被销毁了。 - 系统资源限制:在内存有限的服务器上,浏览器进程可能因占用内存过多而被系统终止。考虑增加服务器资源,或调整
DAEMON_TTL_MS使其更短,更频繁地释放资源。
5.4 与OpenClaw或其他CDP工具冲突
- 症状:端口冲突错误,或者浏览器行为异常。
- 解决:为每个需要CDP端口的工具分配唯一的
BROWSER_DEBUG_PORT。例如,gemini-skill用40821,OpenClaw用18800,其他工具用40822等。确保它们在配置中不重复。
5.5 我的实战配置心得
经过多轮测试,以下是我的稳定运行配置(在macOS/Linux环境下),供你参考:
# .env 配置文件 BROWSER_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome BROWSER_HEADLESS=false # 首次登录后,可改为 true BROWSER_USER_DATA_DIR=./.gemini_browser_data # 使用项目相对路径,便于管理 BROWSER_DEBUG_PORT=40821 BROWSER_PROTOCOL_TIMEOUT=120000 # 生图操作较长,设为2分钟 DAEMON_PORT=40225 DAEMON_TTL_MS=1800000 # 30分钟闲置销毁 OUTPUT_DIR=./output/images # 指定一个清晰的输出目录关键技巧:
- 我将
BROWSER_USER_DATA_DIR设置为项目内的一个目录(.gemini_browser_data)。这样,整个项目(包括配置和用户数据)可以一起打包或迁移,非常方便。 - 首次运行成功后,我会将
BROWSER_HEADLESS改为true,并在后台运行Daemon(例如使用pm2或systemd),将其作为一个常驻服务。 - 定期清理
OUTPUT_DIR目录,避免生成的图片文件占用过多磁盘空间。可以写一个简单的定时任务脚本。
这个项目展示了如何将复杂的浏览器自动化任务进行优雅的工程化封装。它不仅仅是几个脚本的堆砌,而是在设计上考虑了持久化、资源管理、协议标准化和错误处理。如果你对AI应用自动化、RPA(机器人流程自动化)或者MCP生态感兴趣,深入研究gemini-skill的代码会是一个非常好的学习过程。你可以从修改和扩展工具列表开始,例如尝试添加一个自动整理对话历史、导出聊天记录的工具,这将让你更深入地理解CDP和MCP协议是如何协同工作的。