mcpField
G
gemini-research-browser-use

gemini-research-browser-use

github.com/grasseed生产力378 次安装40 周前更新
代码仓库

关于 gemini-research-browser-use

gemini-research-browser-use 是通过 Chrome 浏览器自动化查询 Google Gemini 的技能。它借助 Chrome DevTools Protocol 使用现有登录会话,并将 browser-use CLI 列为替代方式。

主要功能

  1. 01通过 Chrome CDP 发送查询
  2. 02复用已登录的 Chrome 配置
  3. 03提取 Gemini 回复
  4. 04以 browser-use CLI 作为替代方式

适用场景

  1. 01执行需登录的 Gemini 研究查询
  2. 02使用 Chrome 会话自动化查询
  3. 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/)交互。

前置条件

  1. Python + websockets 验证:

    python3 --version
    python3 -m pip show websockets
    

    如果未安装,请安装:

    python3 -m pip install websockets
    
  2. Google Chrome 验证:

    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version
    
  3. CDP 端口可用性 验证 Chrome 正在监听(在步骤 2 中启动后):

    curl -s http://localhost:9222/json | python3 -m json.tool
    
  4. 非默认用户数据目录(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 realCLI 简单打开不带登录状态的新会话❌ 否
browser_subagent可视化反馈有速率限制,可能失败❌ 否

✅ 推荐方法:Chrome 远程调试(CDP)

这是最可靠的方法,使用已登录 Google 账号的系统 Chrome。

前置条件
  1. 安装了带有 websockets 库的 Python 3
  2. Google Chrome 安装在 /Applications/Google Chrome.app/
  3. 用户已在 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 connectChrome 未以调试模式运行使用 --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 directoryChrome 不允许 CDP 使用默认配置文件使用克隆的配置文件启动:/tmp/chrome-gemini-profile
即使 Chrome 正在运行,curl 仍显示连接被拒绝因配置文件路径问题,CDP 未在监听确保 --user-data-dir 不是默认路径,且端口可用
No Gemini page found via CDPGemini 尚未加载或未登录在启动的 Chrome 中打开 https://gemini.google.com/,并等待页面显示 /app
browser-use Issues
ProblemCauseSolution
Not logged inbrowser-use creates isolated sessionUse Chrome Remote Debugging method instead
Unknown key: "請" errorCLI doesn't support UnicodeUse eval with JavaScript execCommand
Click doesn't workElement index changedRe-run state before each click

Best Practices

  1. Always use Chrome Remote Debugging for queries requiring authentication
  2. Wait 30+ seconds for complex queries (Gemini's "Deep Think" mode takes longer)
  3. Check for .markdown elements to verify response is complete
  4. Use inline Python for one-off queries; use the full script for automation
  5. Close Chrome debugging session when done to avoid port conflicts
  6. Keep profile cloned in /tmp/chrome-gemini-profile to 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

最佳實踐:

  1. 每次使用後關閉 Chrome - 避免佔用 9222 端口
  2. 定期清理臨時設定檔 - /tmp/chrome-gemini-profile 可能佔用數百 MB
  3. 保持工作目錄整潔 - 刪除測試腳本,將常用腳本整合到專案中
  4. 使用完整腳本 - 將上述 query_gemini.py 儲存為專案文件,而非每次重新建立

注意事項

  1. 等待時間調整 - 複雜查詢(如深度分析)建議 wait_seconds=60 或更長
  2. 回應截斷問題 - 如果回應很長,可能需要多次提取或使用 get_all_gemini_content.py 方法
  3. 登入狀態 - 確保 Chrome 設定檔中已登入 Google 帳號
  4. 網路穩定性 - CDP 連接需要穩定的網路環境
  5. 並發限制 - 避免同時開啟多個 Chrome 除錯會話在同一端口

评论

支持 Markdown