关于 gemini-research-browser-use
gemini-research-browser-use 是通过 Chrome 浏览器自动化查询 Google Gemini 的技能。它借助 Chrome DevTools Protocol 使用现有登录会话,并将 browser-use CLI 列为替代方式。
主要功能
- 01通过 Chrome CDP 发送查询
- 02复用已登录的 Chrome 配置
- 03提取 Gemini 回复
- 04以 browser-use CLI 作为替代方式
适用场景
- 01执行需登录的 Gemini 研究查询
- 02使用 Chrome 会话自动化查询
- 03提取已有的 Gemini 回复
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ grasseed/ google-search-browser-use --skill gemini-research-browser-use安装后在 Claude Code 中输入/gemini-research-browser-use确认已加载。
详细介绍
Gemini 研究浏览器使用指南
概述
通过 Chrome DevTools Protocol(CDP)使用 Google Gemini 进行研究或查询。此方法会复用用户现有的 Chrome 登录会话,与 Gemini 网页界面(https://gemini.google.com/)交互。
前置条件
-
Python + websockets 验证:
python3 --version python3 -m pip show websockets如果未安装,请安装:
python3 -m pip install websockets -
Google Chrome 验证:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version -
CDP 端口可用性 验证 Chrome 正在监听(在步骤 2 中启动后):
curl -s http://localhost:9222/json | python3 -m json.tool -
非默认用户数据目录(Chrome 要求) Chrome CDP 要求使用非默认配置文件路径。请使用克隆的配置文件,以保留登录状态。
rm -rf /tmp/chrome-gemini-profile rsync -a "$HOME/Library/Application Support/Google/Chrome/" /tmp/chrome-gemini-profile/
方法对比
| 方法 | 优点 | 缺点 | 推荐 |
|---|---|---|---|
| Chrome 远程调试(CDP) | 使用现有登录状态,完整自动化,可靠 | 需要使用调试标志重启 Chrome | ✅ 是 |
browser-use --browser real | CLI 简单 | 打开不带登录状态的新会话 | ❌ 否 |
browser_subagent | 可视化反馈 | 有速率限制,可能失败 | ❌ 否 |
✅ 推荐方法:Chrome 远程调试(CDP)
这是最可靠的方法,使用已登录 Google 账号的系统 Chrome。
前置条件
- 安装了带有
websockets库的 Python 3 - Google Chrome 安装在
/Applications/Google Chrome.app/ - 用户已在 Chrome 中登录 Google
步骤 1:安装 websockets(如有需要)
pip3 install websockets
# Or in virtual environment:
python3 -m venv .venv && ./.venv/bin/pip install websockets
步骤 2:使用远程调试启动 Chrome(非默认配置文件)
重要:请先关闭所有已打开的 Chrome 窗口,或使用其他调试端口。
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir="/tmp/chrome-gemini-profile" \
"https://gemini.google.com/" &
参数说明:
--remote-debugging-port=9222:在端口 9222 上启用 CDP--user-data-dir:指向现有的 Chrome 配置文件(包含登录会话)- 此 URL 会直接打开 Gemini
步骤 3:验证连接(CDP)
curl -s http://localhost:9222/json | python3 -m json.tool
查找 Gemini 页面条目:
{
"title": "Google Gemini",
"url": "https://gemini.google.com/app",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/XXXXXXXX"
}
注意:如果 URL 显示 /app 而不只是 /,则表示你已登录。
步骤 4:向 Gemini 发送查询
将以下内容保存为 gemini_query.py,或直接运行:
import asyncio
import websockets
import json
import subprocess
import sys
async def query_gemini(query_text, wait_seconds=30):
# Get the Gemini page WebSocket URL
result = subprocess.run(
["curl", "-s", "http://localhost:9222/json"],
capture_output=True, text=True
)
pages = json.loads(result.stdout)
# Find Gemini page
gemini_page = None
for page in pages:
if page.get("type") == "page" and "gemini.google.com" in page.get("url", ""):
gemini_page = page
break
if not gemini_page:
print("Error: Gemini page not found. Make sure Chrome is open with Gemini.")
return None
ws_url = gemini_page["webSocketDebuggerUrl"]
print(f"Connecting to: {ws_url}")
async with websockets.connect(ws_url) as ws:
# Step 1: Input the query
input_js = f'''
const editor = document.querySelector('div[contenteditable="true"]');
if(editor) {{
editor.focus();
document.execCommand('insertText', false, `{query_text}`);
editor.dispatchEvent(new Event('input', {{bubbles: true}}));
'success';
}} else {{
'editor not found';
}}
'''
await ws.send(json.dumps({
"id": 1,
"method": "Runtime.evaluate",
"params": {"expression": input_js}
}))
response = await ws.recv()
result = json.loads(response)
print(f"Input result: {result.get('result', {}).get('result', {}).get('value', 'unknown')}")
# Step 2: Click send button
await asyncio.sleep(1)
click_js = '''
const btn = document.querySelector('button[aria-label="傳送訊息"]');
if(btn) { btn.click(); 'clicked'; } else { 'button not found'; }
'''
await ws.send(json.dumps({
"id": 2,
"method": "Runtime.evaluate",
"params": {"expression": click_js}
}))
response = await ws.recv()
result = json.loads(response)
print(f"Click result: {result.get('result', {}).get('result', {}).get('value', 'unknown')}")
# Step 3: Wait for response
print(f"Waiting {wait_seconds} seconds for Gemini to respond...")
await asyncio.sleep(wait_seconds)
# Step 4: Extract the response
extract_js = '''
const markdownEls = document.querySelectorAll('.markdown');
if(markdownEls.length > 0) {
markdownEls[markdownEls.length - 1].innerText;
} else {
'No response found';
}
'''
await ws.send(json.dumps({
"id": 3,
"method": "Runtime.evaluate",
"params": {"expression": extract_js}
}))
response = await ws.recv()
result = json.loads(response)
content = result.get('result', {}).get('result', {}).get('value', 'No content')
return content
# Main execution
if __name__ == "__main__":
query = sys.argv[1] if len(sys.argv) > 1 else "範例問題:請用繁體中文回答什麼是區塊鏈?"
result = asyncio.run(query_gemini(query, wait_seconds=30))
print("\n" + "="*50)
print("GEMINI RESPONSE:")
print("="*50)
print(result)
步骤 5:运行查询
python3 gemini_query.py "範例問題:你的查詢問題"
简单查询也可以直接运行:
python3 << 'EOF'
import asyncio
import websockets
import json
async def send_to_gemini():
# Get WebSocket URL
import subprocess
result = subprocess.run(["curl", "-s", "http://localhost:9222/json"], capture_output=True, text=True)
pages = json.loads(result.stdout)
ws_url = next(p["webSocketDebuggerUrl"] for p in pages if "gemini.google.com" in p.get("url", ""))
async with websockets.connect(ws_url) as ws:
# Input query
await ws.send(json.dumps({
"id": 1,
"method": "Runtime.evaluate",
"params": {"expression": '''
const editor = document.querySelector('div[contenteditable="true"]');
editor.focus();
document.execCommand('insertText', false, '範例問題:請分析比特幣未來的價格走勢');
editor.dispatchEvent(new Event('input', {bubbles: true}));
'''}
}))
await ws.recv()
# Click send
await asyncio.sleep(1)
await ws.send(json.dumps({
"id": 2,
"method": "Runtime.evaluate",
"params": {"expression": '''document.querySelector('button[aria-label="傳送訊息"]').click()'''}
}))
await ws.recv()
# Wait and extract
await asyncio.sleep(30)
await ws.send(json.dumps({
"id": 3,
"method": "Runtime.evaluate",
"params": {"expression": '''
document.querySelectorAll('.markdown')[document.querySelectorAll('.markdown').length - 1].innerText
'''}
}))
response = await ws.recv()
print(json.loads(response)['result']['result']['value'])
asyncio.run(send_to_gemini())
EOF
替代方法:browser-use CLI
此方法更简单,但不会使用现有的 Chrome 登录状态。每次都需要手动登录。
前置条件
# Create virtual environment
python3 -m venv .venv
# Install browser-use
./.venv/bin/pip install browser-use
工作流
1) 打开 Gemini
./.venv/bin/browser-use --browser real open "https://gemini.google.com/"
2) 获取页面状态
./.venv/bin/browser-use --browser real state
查找:
- 输入文本框:
contenteditable=true role=textbox - 发送按钮:
aria-label=傳送訊息
3) 通过 JavaScript eval 输入文本
./.venv/bin/browser-use --browser real eval "const editor = document.querySelector('div[contenteditable=\"true\"]'); editor.focus(); document.execCommand('insertText', false, 'YOUR QUERY HERE'); editor.dispatchEvent(new Event('input', {bubbles: true}));"
4) 点击发送按钮
# Get current state to find button index
./.venv/bin/browser-use --browser real state
# Click the send button (replace INDEX with actual number)
./.venv/bin/browser-use --browser real click INDEX
5) 关闭会话
./.venv/bin/browser-use close
故障排除
Chrome 远程调试问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
curl: (7) Failed to connect | Chrome 未以调试模式运行 | 使用 --remote-debugging-port=9222 重启 Chrome |
| WebSocket 连接被拒绝 | 页面 ID 已更改 | 重新获取 /json,以取得新的 WebSocket URL |
| "editor not found" | 页面尚未完全加载 | 运行脚本前等待几秒 |
| "button not found" | 发送按钮不可见 | 先确认文本是否已实际输入 |
| 显示登录页面而非应用 | user-data-dir 路径错误 | 验证路径:"$HOME/Library/Application Support/Google/Chrome" |
DevTools remote debugging requires a non-default data directory | Chrome 不允许 CDP 使用默认配置文件 | 使用克隆的配置文件启动:/tmp/chrome-gemini-profile |
即使 Chrome 正在运行,curl 仍显示连接被拒绝 | 因配置文件路径问题,CDP 未在监听 | 确保 --user-data-dir 不是默认路径,且端口可用 |
No Gemini page found via CDP | Gemini 尚未加载或未登录 | 在启动的 Chrome 中打开 https://gemini.google.com/,并等待页面显示 /app |
browser-use Issues
| Problem | Cause | Solution |
|---|---|---|
| Not logged in | browser-use creates isolated session | Use Chrome Remote Debugging method instead |
Unknown key: "請" error | CLI doesn't support Unicode | Use eval with JavaScript execCommand |
| Click doesn't work | Element index changed | Re-run state before each click |
Best Practices
- Always use Chrome Remote Debugging for queries requiring authentication
- Wait 30+ seconds for complex queries (Gemini's "Deep Think" mode takes longer)
- Check for
.markdownelements to verify response is complete - Use inline Python for one-off queries; use the full script for automation
- Close Chrome debugging session when done to avoid port conflicts
- Keep profile cloned in
/tmp/chrome-gemini-profileto avoid CDP blocking the default profile
Complete Example: Crypto Price Analysis
完整工作流程
# Step 1: 準備 Chrome 設定檔副本 (避免 CDP 預設目錄限制)
rm -rf /tmp/chrome-gemini-profile
rsync -a "$HOME/Library/Application Support/Google/Chrome/" /tmp/chrome-gemini-profile/
# Step 2: 啟動 Chrome 遠端除錯模式
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir="/tmp/chrome-gemini-profile" \
"https://gemini.google.com/" > /dev/null 2>&1 &
# Step 3: 等待頁面載入並驗證連接
sleep 8
curl -s http://localhost:9222/json | python3 -c "import sys, json; pages = json.load(sys.stdin); gemini = [p for p in pages if p.get('type') == 'page' and 'gemini.google.com' in p.get('url', '')]; print(f\"找到 Gemini 頁面: {gemini[0]['url'] if gemini else '未找到'}\")"
方法 1: 完整查詢腳本 (query_gemini.py)
將以下內容儲存為 query_gemini.py:
import asyncio
import websockets
import json
import subprocess
import sys
async def query_gemini(query_text, wait_seconds=60):
# Get the Gemini page WebSocket URL
result = subprocess.run(
["curl", "-s", "http://localhost:9222/json"],
capture_output=True, text=True
)
pages = json.loads(result.stdout)
# Find Gemini page
gemini_page = None
for page in pages:
if page.get("type") == "page" and "gemini.google.com" in page.get("url", ""):
gemini_page = page
break
if not gemini_page:
print("錯誤:找不到 Gemini 頁面。請確保 Chrome 已開啟 Gemini。")
return None
ws_url = gemini_page["webSocketDebuggerUrl"]
print(f"正在連接到: {ws_url}")
async with websockets.connect(ws_url) as ws:
# Step 1: Input the query
input_js = f'''
const editor = document.querySelector('div[contenteditable="true"]');
if(editor) {{
editor.focus();
document.execCommand('insertText', false, `{query_text}`);
editor.dispatchEvent(new Event('input', {{bubbles: true}}));
'success';
}} else {{
'editor not found';
}}
'''
await ws.send(json.dumps({
"id": 1,
"method": "Runtime.evaluate",
"params": {"expression": input_js}
}))
response = await ws.recv()
result = json.loads(response)
print(f"輸入結果: {result.get('result', {}).get('result', {}).get('value', 'unknown')}")
# Step 2: Click send button
await asyncio.sleep(1)
click_js = '''
const btn = document.querySelector('button[aria-label="傳送訊息"]');
if(btn) { btn.click(); 'clicked'; } else { 'button not found'; }
'''
await ws.send(json.dumps({
"id": 2,
"method": "Runtime.evaluate",
"params": {"expression": click_js}
}))
response = await ws.recv()
result = json.loads(response)
print(f"點擊結果: {result.get('result', {}).get('result', {}).get('value', 'unknown')}")
# Step 3: Wait for response
print(f"等待 {wait_seconds} 秒讓 Gemini 回應...")
await asyncio.sleep(wait_seconds)
# Step 4: Extract the response - try to get complete content
extract_js = '''
const markdownEls = document.querySelectorAll('.markdown');
if(markdownEls.length > 0) {
const lastMarkdown = markdownEls[markdownEls.length - 1];
// Get all text content including nested elements
lastMarkdown.innerText || lastMarkdown.textContent || 'Empty response';
} else {
'No response found';
}
'''
await ws.send(json.dumps({
"id": 3,
"method": "Runtime.evaluate",
"params": {"expression": extract_js}
}))
response = await ws.recv()
result = json.loads(response)
content = result.get('result', {}).get('result', {}).get('value', 'No content')
return content
# Main execution
if __name__ == "__main__":
query = """範例問題:請詳細分析 BTC、ETH 的價格預測走勢。
需包含相關專業指標,並用繁體中文回答。"""
result = asyncio.run(query_gemini(query, wait_seconds=60))
print("\n" + "="*50)
print("GEMINI 回應:")
print("="*50)
print(result)
執行方式:
python3 query_gemini.py
方法 2: 獲取已存在的回應 (get_gemini_response.py)
如果 Gemini 頁面已經有回應,可以使用此腳本直接提取:
import asyncio
import websockets
import json
import subprocess
async def get_all_gemini_content():
# Get the Gemini page WebSocket URL
result = subprocess.run(
["curl", "-s", "http://localhost:9222/json"],
capture_output=True, text=True
)
pages = json.loads(result.stdout)
# Find Gemini page
gemini_page = None
for page in pages:
if page.get("type") == "page" and "gemini.google.com" in page.get("url", ""):
gemini_page = page
break
if not gemini_page:
print("錯誤:找不到 Gemini 頁面。")
return None
ws_url = gemini_page["webSocketDebuggerUrl"]
print(f"正在連接到: {ws_url}\n")
async with websockets.connect(ws_url) as ws:
# Extract all markdown content from the page
extract_js = '''
(function() {
const markdownEls = document.querySelectorAll('.markdown');
console.log('Found markdown elements:', markdownEls.length);
if(markdownEls.length === 0) {
return 'No markdown elements found';
}
// Get the last two markdown elements (user query and AI response)
const responses = [];
const startIdx = Math.max(0, markdownEls.length - 2);
for(let i = startIdx; i < markdownEls.length; i++) {
const text = markdownEls[i].innerText || markdownEls[i].textContent || '';
if(text.trim()) {
responses.push(`[回應 ${i+1}]:\\n${text}`);
}
}
return responses.join('\\n\\n' + '='.repeat(80) + '\\n\\n');
})()
'''
await ws.send(json.dumps({
"id": 1,
"method": "Runtime.evaluate",
"params": {"expression": extract_js, "returnByValue": True}
}))
response = await ws.recv()
result = json.loads(response)
content = result.get('result', {}).get('result', {}).get('value', 'No content')
return content
# Main execution
if __name__ == "__main__":
result = asyncio.run(get_all_gemini_content())
print("="*80)
print("GEMINI 對話內容:")
print("="*80)
print(result)
執行方式:
python3 get_gemini_response.py
實際使用範例
# 完整流程
rm -rf /tmp/chrome-gemini-profile && \
rsync -a "$HOME/Library/Application Support/Google/Chrome/" /tmp/chrome-gemini-profile/ && \
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir="/tmp/chrome-gemini-profile" \
"https://gemini.google.com/" > /dev/null 2>&1 &
# 等待並執行查詢
sleep 8 && python3 query_gemini.py
清理資源
完成查詢後,建議清理臨時文件和資源:
# 1. 關閉 Chrome 除錯會話
pkill -9 "Google Chrome"
# 2. 清理臨時設定檔 (可選,釋放磁碟空間)
rm -rf /tmp/chrome-gemini-profile
# 3. 清理測試過程中生成的臨時腳本和輸出文件
rm -f query_gemini.py get_gemini_response.py get_all_gemini_content.py
rm -f gemini_response.txt gemini_full_response.txt
最佳實踐:
- 每次使用後關閉 Chrome - 避免佔用 9222 端口
- 定期清理臨時設定檔 -
/tmp/chrome-gemini-profile可能佔用數百 MB - 保持工作目錄整潔 - 刪除測試腳本,將常用腳本整合到專案中
- 使用完整腳本 - 將上述
query_gemini.py儲存為專案文件,而非每次重新建立
注意事項
- 等待時間調整 - 複雜查詢(如深度分析)建議
wait_seconds=60或更長 - 回應截斷問題 - 如果回應很長,可能需要多次提取或使用
get_all_gemini_content.py方法 - 登入狀態 - 確保 Chrome 設定檔中已登入 Google 帳號
- 網路穩定性 - CDP 連接需要穩定的網路環境
- 並發限制 - 避免同時開啟多個 Chrome 除錯會話在同一端口