Files
fws_code/tgebrowser_client.py
2026-02-27 04:01:33 +08:00

160 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TgeBrowser API 客户端:用于通过 REST API 新建/启动浏览器环境,获取调试端口。
文档: https://docs.tgebrowser.com/api.html
"""
from __future__ import annotations
import os
from typing import Any, Optional
import requests
# TgeBrowser 本地 API 默认端口
DEFAULT_API_BASE = "http://127.0.0.1:50326"
# API Key写死也可通过环境变量 TGEBROWSER_API_KEY 覆盖)
DEFAULT_API_KEY = "asp_ad11de1727c77a5d514256b3209eec52130c6e5b4f9ccd92"
ENV_API_KEY = "TGEBROWSER_API_KEY"
def get_api_key() -> str:
"""优先从环境变量获取 API Key未配置时使用默认值。"""
key = os.environ.get(ENV_API_KEY)
if key and key.strip():
return key.strip()
return DEFAULT_API_KEY
class TgeBrowserClient:
"""TgeBrowser 本地 API 客户端。"""
def __init__(self, base_url: str = DEFAULT_API_BASE, api_key: Optional[str] = None):
self.base_url = base_url.rstrip("/")
self.api_key = api_key or get_api_key()
self._headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
def _post(self, path: str, json: Optional[dict] = None) -> dict:
resp = requests.post(
f"{self.base_url}{path}",
headers=self._headers,
json=json or {},
timeout=30,
)
resp.raise_for_status()
data = resp.json()
if not data.get("success", False):
msg = data.get("message", "未知错误")
raise RuntimeError(f"TgeBrowser API 失败: {msg}")
return data
def _get(self, path: str) -> dict:
resp = requests.get(
f"{self.base_url}{path}",
headers=self._headers,
timeout=30,
)
resp.raise_for_status()
data = resp.json()
if not data.get("success", False):
msg = data.get("message", "未知错误")
raise RuntimeError(f"TgeBrowser API 失败: {msg}")
return data
def status(self) -> dict:
"""检查 API 服务状态。"""
return self._get("/api/status")
def create_browser(
self,
browser_name: str = "miguSM_automation",
start_page_url: Optional[str] = None,
**kwargs: Any,
) -> dict:
"""
创建新的浏览器环境。
返回 data.envId 用于后续 start。
"""
# 使用 TgeBrowser 文档要求的完整结构,避免 400
start_page_value = [start_page_url] if start_page_url else []
start_page_mode = "custom" if start_page_value else "none"
# 指定手机端指纹,随机生成手机 UA
mobile_fingerprint = {
"os": "Android",
"platformVersion": 12,
}
payload = {
"browserName": browser_name,
"proxy": {"protocol": "direct"},
"fingerprint": mobile_fingerprint,
"startInfo": {
"startPage": {"mode": start_page_mode, "value": start_page_value},
"otherConfig": {"openConfigPage": False, "checkPage": False},
},
**kwargs,
}
data = self._post("/api/browser/create", json=payload)
return data.get("data", {})
def start_browser(
self,
env_id: Optional[int] = None,
user_index: Optional[int] = None,
port: Optional[int] = None,
**kwargs: Any,
) -> dict:
"""
启动浏览器环境。
返回 data 含 ws、port、http 等,用于 DrissionPage 连接。
"""
payload: dict[str, Any] = {}
if env_id is not None:
payload["envId"] = env_id
elif user_index is not None:
payload["userIndex"] = user_index
else:
raise ValueError("必须指定 envId 或 userIndex")
if port is not None:
payload["port"] = port
payload.update(kwargs)
data = self._post("/api/browser/start", json=payload)
return data.get("data", {})
def stop_browser(self, env_id: Optional[int] = None, user_index: Optional[int] = None) -> dict:
"""停止浏览器环境。"""
payload: dict[str, Any] = {}
if env_id is not None:
payload["envId"] = env_id
elif user_index is not None:
payload["userIndex"] = user_index
else:
raise ValueError("必须指定 envId 或 userIndex")
return self._post("/api/browser/stop", json=payload)
def create_and_start(
self,
browser_name: str = "miguSM_automation",
start_page_url: Optional[str] = None,
) -> dict:
"""
创建并启动新浏览器,每次调用都会新建一个环境。
返回含 ws、port、envId 等,供 DrissionPage 连接。
"""
create_data = self.create_browser(
browser_name=browser_name,
start_page_url=start_page_url,
)
env_id = create_data.get("envId")
if env_id is None:
raise RuntimeError("创建浏览器失败:未返回 envId")
start_data = self.start_browser(env_id=env_id)
start_data["envId"] = env_id
return start_data