245 lines
7.5 KiB
Markdown
245 lines
7.5 KiB
Markdown
# 浏览器使用规范
|
||
|
||
> 📅 创建日期:2026-02-13
|
||
> 📋 全局强制规范:所有技能中涉及浏览器操作时,必须遵守本文档
|
||
|
||
---
|
||
|
||
## 一、核心原则
|
||
|
||
| 原则 | 说明 |
|
||
|:---|:---|
|
||
| **使用系统默认浏览器** | 不硬编码任何特定浏览器,用 `open` 命令自动调用 |
|
||
| **复用已有会话** | 使用用户已登录的浏览器实例,保留 Cookie/登录态 |
|
||
| **禁止新建实例** | 不创建无痕/隔离/Headless 浏览器窗口 |
|
||
| **API 优先** | 能用 API 完成的,绝不启动浏览器 |
|
||
|
||
---
|
||
|
||
## 二、操作方式优先级
|
||
|
||
### 优先级 1:命令行 API 调用(最优先)
|
||
|
||
```bash
|
||
# 直接用 curl 调用 API,完全不需要浏览器
|
||
curl -X POST "https://api.example.com/endpoint" \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"key": "value"}'
|
||
```
|
||
|
||
```python
|
||
# Python requests 直接调 API
|
||
import requests
|
||
resp = requests.post(url, headers=headers, json=data)
|
||
```
|
||
|
||
### 优先级 2:系统默认浏览器打开 URL
|
||
|
||
```bash
|
||
# macOS 用 open 命令(自动调用系统默认浏览器)
|
||
open "https://example.com"
|
||
|
||
# 如果需要指定应用(不推荐,仅特殊情况)
|
||
# open -a "Safari" "https://example.com"
|
||
```
|
||
|
||
```python
|
||
import subprocess
|
||
# 用系统默认浏览器打开 URL
|
||
subprocess.run(['open', 'https://example.com'])
|
||
```
|
||
|
||
### 优先级 3:AppleScript 控制已打开的浏览器
|
||
|
||
```python
|
||
import subprocess
|
||
|
||
def get_default_browser_name():
|
||
"""获取默认浏览器名称"""
|
||
import json, os
|
||
r = subprocess.run(['plutil', '-convert', 'json', '-o', '-',
|
||
os.path.expanduser('~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist')],
|
||
capture_output=True, text=True)
|
||
if r.returncode == 0:
|
||
data = json.loads(r.stdout)
|
||
for h in data.get('LSHandlers', []):
|
||
if h.get('LSHandlerURLScheme') == 'https':
|
||
bundle_id = h.get('LSHandlerRoleAll', '')
|
||
# bundle ID → 应用名映射
|
||
mapping = {
|
||
'com.google.chrome': 'Google Chrome',
|
||
'com.apple.safari': 'Safari',
|
||
'com.brave.browser': 'Brave Browser',
|
||
'com.microsoft.edgemac': 'Microsoft Edge',
|
||
'company.thebrowser.browser': 'Arc',
|
||
'com.bot.pc.doubao.browser': '豆包浏览器',
|
||
'org.mozilla.firefox': 'Firefox',
|
||
}
|
||
return mapping.get(bundle_id, bundle_id)
|
||
return 'Safari'
|
||
|
||
def open_url_in_default_browser(url):
|
||
"""用系统默认浏览器打开 URL(复用已有窗口)"""
|
||
subprocess.run(['open', url])
|
||
|
||
def get_current_url():
|
||
"""获取默认浏览器当前标签页 URL(AppleScript)"""
|
||
browser = get_default_browser_name()
|
||
if browser == 'Google Chrome':
|
||
script = 'tell application "Google Chrome" to get URL of active tab of front window'
|
||
elif browser == 'Safari':
|
||
script = 'tell application "Safari" to get URL of current tab of front window'
|
||
else:
|
||
# 通用方案:通过 System Events
|
||
script = f'''
|
||
tell application "System Events"
|
||
tell process "{browser}"
|
||
-- 尝试获取地址栏内容
|
||
end tell
|
||
end tell
|
||
'''
|
||
r = subprocess.run(['osascript', '-e', script], capture_output=True, text=True)
|
||
return r.stdout.strip()
|
||
```
|
||
|
||
### 优先级 4:Cursor MCP Browser 工具
|
||
|
||
使用 Cursor 内置的 `cursor-ide-browser` MCP 服务进行浏览器交互。
|
||
|
||
### 优先级 5:CDP 连接已运行浏览器(最后手段)
|
||
|
||
仅在必须进行 DOM 自动化操作时使用,且必须连接已运行的浏览器:
|
||
|
||
```python
|
||
from playwright.sync_api import sync_playwright
|
||
|
||
# ⚠️ 仅连接已运行的浏览器,不启动新实例
|
||
# 需要浏览器已开启远程调试端口
|
||
with sync_playwright() as p:
|
||
browser = p.chromium.connect_over_cdp("http://localhost:9222")
|
||
context = browser.contexts[0] # 使用已有的 context
|
||
page = context.pages[0] # 使用已有的 page
|
||
```
|
||
|
||
---
|
||
|
||
## 三、检测系统默认浏览器
|
||
|
||
```python
|
||
import subprocess, json, os
|
||
|
||
def get_default_browser():
|
||
"""获取 macOS 系统默认浏览器 bundle ID 和名称"""
|
||
r = subprocess.run(['plutil', '-convert', 'json', '-o', '-',
|
||
os.path.expanduser('~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist')],
|
||
capture_output=True, text=True)
|
||
|
||
bundle_id = 'com.apple.Safari' # 默认 Safari
|
||
if r.returncode == 0:
|
||
data = json.loads(r.stdout)
|
||
for h in data.get('LSHandlers', []):
|
||
if h.get('LSHandlerURLScheme') == 'https':
|
||
bundle_id = h.get('LSHandlerRoleAll', 'com.apple.Safari')
|
||
break
|
||
|
||
# 常见浏览器 bundle ID → 名称
|
||
browser_names = {
|
||
'com.google.chrome': 'Google Chrome',
|
||
'com.apple.safari': 'Safari',
|
||
'com.brave.browser': 'Brave Browser',
|
||
'com.microsoft.edgemac': 'Microsoft Edge',
|
||
'company.thebrowser.browser': 'Arc',
|
||
'com.bot.pc.doubao.browser': '豆包浏览器',
|
||
'org.mozilla.firefox': 'Firefox',
|
||
'com.opera.opera': 'Opera',
|
||
}
|
||
|
||
return {
|
||
'bundle_id': bundle_id,
|
||
'name': browser_names.get(bundle_id, bundle_id),
|
||
}
|
||
|
||
# 当前系统默认浏览器:豆包浏览器 (com.bot.pc.doubao.browser)
|
||
```
|
||
|
||
---
|
||
|
||
## 四、禁止行为清单
|
||
|
||
| 禁止行为 | 说明 |
|
||
|:---|:---|
|
||
| `playwright.chromium.launch()` | 会创建全新的隔离浏览器实例 |
|
||
| `playwright.chromium.launch_persistent_context()` | 虽然复用数据目录,但会锁定 profile 导致冲突 |
|
||
| `webdriver.Chrome()` | 创建新的 ChromeDriver 实例 |
|
||
| `--incognito` / `--inprivate` | 无痕模式,丢失所有登录态 |
|
||
| `headless=True` | 无界面模式,无法复用已有会话 |
|
||
| 硬编码浏览器路径 | 如 `/Applications/Google Chrome.app/...` |
|
||
|
||
---
|
||
|
||
## 五、各技能适配说明
|
||
|
||
| 技能 | 原方案 | 新方案 |
|
||
|:---|:---|:---|
|
||
| 网站逆向分析 | Playwright launch + Stealth | API 调用优先;需要浏览器时用 `open` + AppleScript |
|
||
| 流量自动化 | Selenium WebDriver | API 调用优先;需要浏览器时用 `open` + AppleScript |
|
||
| 飞书管理 | Playwright 无痕窗口 | API 调用(飞书 Open API);需要浏览器时用 `open` |
|
||
| 存客宝 | 无浏览器依赖 | 无需修改 |
|
||
|
||
---
|
||
|
||
## 六、常见场景处理
|
||
|
||
### 场景1:需要用户登录后操作
|
||
|
||
```bash
|
||
# 直接用默认浏览器打开登录页(用户已有登录态则自动跳过)
|
||
open "https://project.feishu.cn/settings"
|
||
```
|
||
|
||
### 场景2:需要自动填表/点击
|
||
|
||
```python
|
||
# 优先用 API 完成
|
||
# 若必须用浏览器,用 AppleScript 操作已打开的页面
|
||
import subprocess
|
||
|
||
script = '''
|
||
tell application "System Events"
|
||
tell process "豆包浏览器"
|
||
-- 模拟键盘/鼠标操作
|
||
keystroke "v" using command down -- 粘贴
|
||
delay 1
|
||
keystroke return -- 回车
|
||
end tell
|
||
end tell
|
||
'''
|
||
subprocess.run(['osascript', '-e', script])
|
||
```
|
||
|
||
### 场景3:需要获取浏览器中的数据
|
||
|
||
```python
|
||
# 用 API 获取(最佳)
|
||
# 或用 AppleScript 获取当前页面信息
|
||
import subprocess
|
||
|
||
script = '''
|
||
tell application "Google Chrome"
|
||
set pageSource to execute active tab of front window javascript "document.title"
|
||
return pageSource
|
||
end tell
|
||
'''
|
||
result = subprocess.run(['osascript', '-e', script], capture_output=True, text=True)
|
||
print(result.stdout.strip())
|
||
```
|
||
|
||
---
|
||
|
||
## 七、版本记录
|
||
|
||
| 日期 | 版本 | 变更 |
|
||
|:---|:---|:---|
|
||
| 2026-02-13 | v1.0 | 初版,明确默认浏览器优先、禁止新建实例 |
|