136 lines
4.0 KiB
Python
136 lines
4.0 KiB
Python
"""浏览器会话启动(系统 Chrome/Edge + Playwright persistent context)。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
import os
|
||
from typing import Any, Optional, Tuple
|
||
|
||
from util.constants import DEFAULT_START_URL
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
DEFAULT_PAGE_TIMEOUT_MS = 15_000
|
||
GOTO_TIMEOUT_MS = 60_000
|
||
|
||
STEALTH_INIT_SCRIPT = """
|
||
(() => {
|
||
try { Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); } catch (e) {}
|
||
})();
|
||
"""
|
||
|
||
|
||
def get_start_url() -> str:
|
||
return (os.getenv("TARGET_START_URL") or DEFAULT_START_URL).strip() or DEFAULT_START_URL
|
||
|
||
|
||
def _headless_from_env() -> bool:
|
||
v = (os.getenv("OPENCLAW_BROWSER_HEADLESS") or "0").strip().lower()
|
||
return v in ("1", "true", "yes", "on")
|
||
|
||
|
||
def _find_chrome_executable() -> str | None:
|
||
try:
|
||
from jiangchang_skill_core.runtime_env import find_chrome_executable
|
||
|
||
chrome = find_chrome_executable()
|
||
if chrome and os.path.isfile(chrome):
|
||
return chrome
|
||
except ImportError:
|
||
pass
|
||
|
||
candidates = [
|
||
os.path.expandvars(r"%ProgramFiles%\Google\Chrome\Application\chrome.exe"),
|
||
os.path.expandvars(r"%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe"),
|
||
os.path.expandvars(r"%ProgramFiles%\Microsoft\Edge\Application\msedge.exe"),
|
||
os.path.expandvars(r"%ProgramFiles(x86)%\Microsoft\Edge\Application\msedge.exe"),
|
||
]
|
||
for path in candidates:
|
||
if path and os.path.isfile(path):
|
||
return path
|
||
return None
|
||
|
||
|
||
async def start_browser_session(
|
||
profile_dir: str,
|
||
*,
|
||
headless: Optional[bool] = None,
|
||
) -> Tuple[Any, Any, Any]:
|
||
"""
|
||
启动持久化 Chrome profile,new_page + goto 入口页,返回 (playwright, context, page)。
|
||
|
||
真实 skill 需要安装 playwright 与 jiangchang_skill_core。
|
||
"""
|
||
from playwright.async_api import async_playwright
|
||
|
||
hl = headless if headless is not None else _headless_from_env()
|
||
start_url = get_start_url()
|
||
chrome = _find_chrome_executable()
|
||
if not chrome:
|
||
raise RuntimeError(
|
||
"ERROR:MISSING_BROWSER 未检测到 Chrome 或 Edge 浏览器,请先安装系统浏览器后重试。"
|
||
)
|
||
|
||
launch_args = ["--start-maximized", "--disable-blink-features=AutomationControlled"]
|
||
logger.info(
|
||
"browser_launch chrome=%s profile_dir=%s start_url=%s args=%s headless=%s",
|
||
chrome,
|
||
profile_dir,
|
||
start_url,
|
||
launch_args,
|
||
hl,
|
||
)
|
||
print(f"[浏览器] 系统 Chrome: {chrome}")
|
||
print(f"[浏览器] profile_dir: [{profile_dir}]")
|
||
print(f"[浏览器] startup_url={start_url}")
|
||
print(f"[浏览器] args={launch_args}")
|
||
|
||
pw = await async_playwright().start()
|
||
context = None
|
||
try:
|
||
context = await pw.chromium.launch_persistent_context(
|
||
user_data_dir=profile_dir,
|
||
headless=hl,
|
||
executable_path=chrome,
|
||
locale="zh-CN",
|
||
no_viewport=True,
|
||
args=launch_args,
|
||
ignore_default_args=["--enable-automation"],
|
||
)
|
||
await context.add_init_script(STEALTH_INIT_SCRIPT)
|
||
|
||
page = await context.new_page()
|
||
page.set_default_timeout(DEFAULT_PAGE_TIMEOUT_MS)
|
||
|
||
try:
|
||
await page.goto(start_url, wait_until="domcontentloaded", timeout=GOTO_TIMEOUT_MS)
|
||
except Exception as exc:
|
||
raise RuntimeError(
|
||
f"浏览器已启动,但未能打开目标首页 {start_url}。"
|
||
"请检查网络、Chrome profile、代理或页面访问状态。"
|
||
f"({exc})"
|
||
) from exc
|
||
|
||
return pw, context, page
|
||
except Exception:
|
||
if context is not None:
|
||
try:
|
||
await context.close()
|
||
except Exception:
|
||
pass
|
||
await pw.stop()
|
||
raise
|
||
|
||
|
||
async def close_browser_context(pw, context) -> None:
|
||
try:
|
||
if context is not None:
|
||
await context.close()
|
||
except Exception:
|
||
pass
|
||
try:
|
||
if pw is not None:
|
||
await pw.stop()
|
||
except Exception:
|
||
pass
|