1. 项目背景与核心价值
最近发现一个很有意思的技术方案——Hermes项目,它能够让你直接用命令行接管已经登录的Chrome浏览器会话。这意味着你再也不用为了自动化操作而专门启动一个无头浏览器或者云浏览器实例了。
这个方案最吸引我的地方在于:它完美解决了浏览器自动化中的登录态保持问题。做过爬虫或者自动化测试的朋友都知道,处理那些需要登录的网站是最头疼的。每次都要重新模拟登录流程,还要应对各种验证码和风控机制。而Hermes的思路是直接复用用户已经登录的Chrome会话,简直是太聪明了!
2. 技术原理深度解析
2.1 Chrome远程调试协议
Hermes的核心是建立在Chrome DevTools Protocol(CDP)之上的。这是Chrome内置的一个调试接口,默认监听在9222端口。当你启动Chrome时加上--remote-debugging-port=9222参数,就能开启这个功能。
CDP提供了完整的浏览器控制能力:
- 页面导航控制
- DOM查询与操作
- 网络请求拦截
- JavaScript执行环境
- 性能分析数据
2.2 会话接管机制
Hermes的魔法在于它能够发现并连接到已经运行的Chrome实例。具体流程是这样的:
- 首先扫描本地9222端口(或其他指定端口)
- 通过
/json接口获取所有打开的标签页信息 - 找到目标页面的WebSocket调试URL
- 建立WebSocket连接开始控制会话
这个过程中最精妙的是它不需要任何额外的浏览器插件或扩展,完全利用Chrome原生支持的功能。
3. 完整安装与配置指南
3.1 基础环境准备
首先确保你的系统已经安装:
- Chrome浏览器(版本92+)
- Node.js 16+(Hermes是基于Node实现的)
- 一个终端环境(CMD/PowerShell/iTerm等)
3.2 Hermes安装步骤
npm install -g hermes-chrome安装完成后,建议先关闭所有Chrome窗口,然后用调试模式重新启动:
# MacOS/Linux /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 # Windows "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --remote-debugging-port=92223.3 首次连接测试
保持Chrome运行状态,在另一个终端执行:
hermes attach如果一切正常,你会看到类似这样的输出:
Found 3 Chrome tabs: [1] https://mail.google.com [2] https://github.com [3] https://twitter.com Select tab to attach (1-3):4. 高级使用场景
4.1 自动化登录态保持
假设你经常需要从Gmail提取数据,传统方式需要处理OAuth流程。用Hermes可以这样做:
- 手动登录Gmail(只需一次)
- 创建自动化脚本:
const hermes = require('hermes-chrome'); (async () => { const browser = await hermes.connect(); const page = browser.pages[0]; // 获取第一个标签页 // 如果当前不是Gmail就导航过去 if(!page.url.includes('mail.google.com')) { await page.navigate('https://mail.google.com'); } // 执行你的自动化操作 const unreadCount = await page.evaluate(() => { return document.title.match(/(\d+) unread/)[1]; }); console.log(`你有${unreadCount}封未读邮件`); })();4.2 结合爬虫框架
Hermes可以和Puppeteer、Playwright等工具无缝集成:
const puppeteer = require('puppeteer-core'); const hermes = require('hermes-chrome'); (async () => { const endpoint = await hermes.getDebuggerUrl(); const browser = await puppeteer.connect({ browserWSEndpoint: endpoint }); // 现在你可以使用完整的Puppeteer API const page = browser.pages()[0]; await page.screenshot({path: 'screenshot.png'}); })();5. 安全注意事项
虽然这个方案很方便,但必须注意以下几点:
- 不要在生产服务器启用调试端口- 这会让你的浏览器完全暴露在网络上
- 使用后及时关闭调试模式- 日常浏览时不需要保持9222端口开放
- 注意敏感信息泄露- 自动化脚本可以获取页面中的所有数据,包括cookies
- 建议使用独立配置- 可以创建专门的Chrome用户目录用于调试:
chrome --user-data-dir=/path/to/debug-profile --remote-debugging-port=92226. 性能优化技巧
6.1 多标签页管理
当同时控制多个标签页时,建议:
// 获取所有标签页 const pages = await browser.pages(); // 按URL过滤目标页面 const targetPage = pages.find(p => p.url.includes('target-site.com')); // 如果没有就新建一个 if(!targetPage) { targetPage = await browser.newPage(); await targetPage.goto('https://target-site.com'); }6.2 资源控制
避免内存泄漏的几个建议:
- 定期清理不再使用的页面
- 使用
page.close()关闭不需要的标签页 - 对于长时间运行的脚本,定时刷新页面释放内存
7. 常见问题排查
7.1 连接失败问题
如果hermes attach报错,检查:
- Chrome是否以调试模式启动
- 防火墙是否阻止了本地9222端口
- 是否有多个Chrome实例在运行(建议全部关闭后重试)
7.2 页面无响应
遇到页面卡死的情况可以:
- 尝试重新连接:
hermes reconnect - 检查Chrome任务管理器(Shift+Esc)是否有页面卡住
- 在DevTools中手动执行
location.reload(true)强制刷新
7.3 跨域限制
虽然可以接管已有会话,但浏览器安全策略仍然有效。如果需要跨域请求,还是需要通过常规方式处理CORS。
8. 实际应用案例
8.1 社交媒体自动化
假设你要监控Twitter动态:
const hermes = require('hermes-chrome'); setInterval(async () => { const browser = await hermes.connect(); const twitterPage = browser.pages.find(p => p.url.includes('twitter.com')); if(twitterPage) { const newTweets = await twitterPage.evaluate(() => { return [...document.querySelectorAll('[data-testid="tweet"]')] .map(el => el.innerText); }); console.log('最新推文:', newTweets[0]); } }, 60000); // 每分钟检查一次8.2 电商价格监控
跟踪商品价格变化:
const previousPrices = {}; async function checkPrices() { const browser = await hermes.connect(); const amazonPage = browser.pages.find(p => p.url.includes('amazon.com')); if(amazonPage) { const productInfo = await amazonPage.evaluate(() => { return { name: document.getElementById('productTitle')?.innerText.trim(), price: document.querySelector('.a-price-whole')?.innerText }; }); if(productInfo.name && previousPrices[productInfo.name] !== productInfo.price) { console.log(`价格变化: ${productInfo.name} 从 ${previousPrices[productInfo.name]} 变为 ${productInfo.price}`); previousPrices[productInfo.name] = productInfo.price; } } }9. 替代方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Hermes | 无需重新登录,直接复用现有会话 | 依赖Chrome调试接口 | 需要保持登录态的自动化 |
| Puppeteer | 功能完整,社区支持好 | 需要处理登录流程 | 全新的自动化任务 |
| Selenium | 支持多语言,跨浏览器 | 配置复杂,性能较低 | 企业级测试套件 |
| Playwright | 现代API,多浏览器支持 | 学习曲线较陡 | 复杂的跨浏览器自动化 |
10. 进阶开发指南
如果你想基于Hermes进行二次开发,核心API包括:
const hermes = require('hermes-chrome'); // 连接到指定端口的Chrome实例 const browser = await hermes.connect({ port: 9222, // 默认值 host: 'localhost' // 也可以是远程地址 }); // 获取所有标签页 const pages = await browser.pages(); // 创建新标签页 const newPage = await browser.newPage(); // 执行CDP原始命令 const {result} = await browser.send('Runtime.evaluate', { expression: 'navigator.userAgent' });对于更复杂的需求,可以直接操作CDP协议:
// 订阅网络请求事件 await browser.send('Network.enable'); browser.on('Network.requestWillBeSent', event => { console.log('请求发出:', event.request.url); }); // 修改User-Agent await browser.send('Network.setUserAgentOverride', { userAgent: 'Mozilla/5.0 (Custom Agent)' });11. 个人使用心得
在实际使用中,我发现这个工具特别适合以下几类场景:
- 需要频繁登录的网站- 比如公司内网、需要2FA验证的系统
- 临时性自动化需求- 快速写个脚本处理当前页面数据
- 调试复杂页面状态- 复现用户实际遇到的界面问题
有个小技巧:可以创建一个alias简化启动命令:
# 在.bashrc或.zshrc中添加 alias chrome-debug='/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 &'这样以后只需要输入chrome-debug就能以调试模式启动Chrome了。