gemini-research-browser-use について
gemini-research-browser-useは、Chromeのブラウザー自動化でGoogle Geminiに問い合わせるスキルです。Chrome DevTools Protocolで既存のログインセッションを使い、代替手段としてbrowser-use CLIも説明します。
主な機能
- 01Chrome CDP経由のクエリ送信
- 02ログイン済みChromeプロファイルの再利用
- 03Geminiの応答抽出
- 04代替手段としてのbrowser-use CLI
活用シーン
- 01ログインが必要なGeminiリサーチクエリ
- 02Chromeセッションを使ったクエリ自動化
- 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/)を操作します。
前提条件
-
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 Remote Debugging(CDP) | 既存のログインを使用し、完全に自動化でき、信頼性が高い | デバッグフラグを付けて Chrome を再起動する必要がある | ✅ はい |
browser-use --browser real | CLI がシンプル | ログインしていない新しいセッションが開く | ❌ いいえ |
browser_subagent | 視覚的なフィードバック | レート制限があり、失敗する場合がある | ❌ いいえ |
✅ 推奨方法:Chrome Remote Debugging(CDP)
既存の Google ログインを使用するシステム上の Chrome を使う、最も信頼性の高い方法です。
前提条件
websocketsライブラリを含む Python 3/Applications/Google Chrome.app/にインストールされた Google Chrome- 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 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 の問題
| 問題 | 原因 | 解決策 |
|---|---|---|
| ログインしていない | browser-use は分離されたセッションを作成します | 代わりに Chrome Remote Debugging の方法を使用してください |
Unknown key: "請" エラー | CLI は Unicode をサポートしていません | JavaScript の execCommand を使用して eval を実行してください |
| クリックが機能しない | 要素のインデックスが変わりました | クリックするたびに state を再実行してください |
ベストプラクティス
- 認証が必要なクエリでは、必ず Chrome Remote Debugging を使用してください
- 複雑なクエリでは 30 秒以上待ってください(Gemini の「Deep Think」モードは時間がかかります)
- 回答が完全かどうかを確認するため、
.markdown要素を確認してください - 1 回限りのクエリには インライン Python を使用し、自動化には完全なスクリプトを使用してください
- ポートの競合を避けるため、完了後に Chrome のデバッグセッションを閉じてください
- 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
ベストプラクティス:
- 使用後は毎回 Chrome を閉じてください - 9222 ポートの占有を避けます
- 一時プロファイルを定期的にクリーンアップしてください -
/tmp/chrome-gemini-profileは数百 MB を占有することがあります - 作業ディレクトリを整理してください - テストスクリプトを削除し、よく使うスクリプトをプロジェクトにまとめます
- 完全なスクリプトを使用してください - 上記の
query_gemini.pyを毎回作成し直すのではなく、プロジェクトのファイルとして保存します
注意事項
- 待機時間の調整 - 複雑なクエリ(詳細な分析など)では、
wait_seconds=60以上をおすすめします - 回答の切り詰め - 回答が長い場合、複数回に分けて抽出するか、
get_all_gemini_content.pyを使用する方法が必要になることがあります - ログイン状態 - Chrome プロファイルで Google アカウントにログインしていることを確認してください
- ネットワークの安定性 - CDP 接続には安定したネットワーク環境が必要です
- 同時実行の制限 - 同じポートで複数の Chrome デバッグセッションを同時に開かないでください