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. 01Chrome CDP経由のクエリ送信
  2. 02ログイン済みChromeプロファイルの再利用
  3. 03Geminiの応答抽出
  4. 04代替手段としてのbrowser-use CLI

活用シーン

  1. 01ログインが必要なGeminiリサーチクエリ
  2. 02Chromeセッションを使ったクエリ自動化
  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 Research Browser Use

概要

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 Remote Debugging(CDP)既存のログインを使用し、完全に自動化でき、信頼性が高いデバッグフラグを付けて Chrome を再起動する必要がある✅ はい
browser-use --browser realCLI がシンプルログインしていない新しいセッションが開く❌ いいえ
browser_subagent視覚的なフィードバックレート制限があり、失敗する場合がある❌ いいえ

✅ 推奨方法:Chrome Remote Debugging(CDP)

既存の Google ログインを使用するシステム上の Chrome を使う、最も信頼性の高い方法です。

前提条件
  1. websockets ライブラリを含む Python 3
  2. /Applications/Google Chrome.app/ にインストールされた Google Chrome
  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 Remote Debugging の問題
問題原因解決策
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 の問題
問題原因解決策
ログインしていないbrowser-use は分離されたセッションを作成します代わりに Chrome Remote Debugging の方法を使用してください
Unknown key: "請" エラーCLI は Unicode をサポートしていませんJavaScript の execCommand を使用して eval を実行してください
クリックが機能しない要素のインデックスが変わりましたクリックするたびに state を再実行してください

ベストプラクティス

  1. 認証が必要なクエリでは、必ず Chrome Remote Debugging を使用してください
  2. 複雑なクエリでは 30 秒以上待ってください(Gemini の「Deep Think」モードは時間がかかります)
  3. 回答が完全かどうかを確認するため、.markdown 要素を確認してください
  4. 1 回限りのクエリには インライン Python を使用し、自動化には完全なスクリプトを使用してください
  5. ポートの競合を避けるため、完了後に Chrome のデバッグセッションを閉じてください
  6. CDP によるデフォルトプロファイルのブロックを避けるため、プロファイルのクローンを /tmp/chrome-gemini-profile に保持してください

完全な例:暗号資産の価格分析

完全なワークフロー
# 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 デバッグセッションを同時に開かないでください

コメント