diff --git a/.env.example b/.env.example index 4f9d94361..0a67d317f 100644 --- a/.env.example +++ b/.env.example @@ -85,3 +85,9 @@ GRAPHRAG_ENABLED=false # 仅在 GRAPHRAG_ENABLED=True 时生效 # 一般推荐设置:2~4 GRAPHRAG_MAX_QUERIES=3 + +# wuying-agentbay-sdk 配置(用于CDP模式的远程浏览器session) +# AgentBay 镜像ID,默认为 browser_latest +AGENTBAY_IMAGE_ID=browser_latest +# AgentBay API密钥,用于创建远程浏览器session +AGENTBAY_API_KEY= \ No newline at end of file diff --git a/MindSpider/DeepSentimentCrawling/MediaCrawler/config/base_config.py b/MindSpider/DeepSentimentCrawling/MediaCrawler/config/base_config.py index dbea153a1..f9cd61d74 100644 --- a/MindSpider/DeepSentimentCrawling/MediaCrawler/config/base_config.py +++ b/MindSpider/DeepSentimentCrawling/MediaCrawler/config/base_config.py @@ -39,7 +39,13 @@ # 这种方式使用真实的浏览器环境,包括用户的扩展、Cookie和设置,大大降低被检测的风险 ENABLE_CDP_MODE = True -# CDP调试端口,用于与浏览器通信 +# 是否使用 wuying-agentbay-sdk 创建 session 并连接浏览器 +# 如果设置为 True,将使用 wuying-agentbay-sdk 创建远程浏览器 session +# 需要设置 AGENTBAY_API_KEY +# 如果设置为 False,将使用本地浏览器通过 CDP 连接 +ENABLE_WUYING_CDP_MODE = False + +# CDP调试端口,用于与浏览器通信(仅本地浏览器模式) # 如果端口被占用,系统会自动尝试下一个可用端口 CDP_DEBUG_PORT = 9222 diff --git "a/MindSpider/DeepSentimentCrawling/MediaCrawler/docs/CDP\346\250\241\345\274\217\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/MindSpider/DeepSentimentCrawling/MediaCrawler/docs/CDP\346\250\241\345\274\217\344\275\277\347\224\250\346\214\207\345\215\227.md" index 541cbc339..68a53846d 100644 --- "a/MindSpider/DeepSentimentCrawling/MediaCrawler/docs/CDP\346\250\241\345\274\217\344\275\277\347\224\250\346\214\207\345\215\227.md" +++ "b/MindSpider/DeepSentimentCrawling/MediaCrawler/docs/CDP\346\250\241\345\274\217\344\275\277\347\224\250\346\214\207\345\215\227.md" @@ -16,12 +16,21 @@ CDP(Chrome DevTools Protocol)模式是一种高级的反检测爬虫技术 ### 1. 启用CDP模式 +CDP模式支持两种方式: +- **本地浏览器模式**: 使用本地安装的Chrome/Edge浏览器 +- **wuying-agentbay-sdk模式**: 使用远程浏览器session(需要API Key) + +#### 方式一:本地浏览器模式 + 在 `config/base_config.py` 中设置: ```python # 启用CDP模式 ENABLE_CDP_MODE = True +# 使用本地浏览器(默认) +ENABLE_WUYING_CDP_MODE = False + # CDP调试端口(可选,默认9222) CDP_DEBUG_PORT = 9222 @@ -32,6 +41,38 @@ CDP_HEADLESS = False AUTO_CLOSE_BROWSER = True ``` +#### 方式二:wuying-agentbay-sdk模式 + +1. **安装 wuying-agentbay-sdk**: +```bash +# 直接安装 PyPI 版本(推荐) +pip install wuying-agentbay-sdk +``` +注意:代码已自动支持同步和异步两种版本的 SDK,会自动适配。 + +2. **配置API Key**: +```python +# 在 config/base_config.py 中设置 +ENABLE_CDP_MODE = True +ENABLE_WUYING_CDP_MODE = True +AGENTBAY_API_KEY = "your_api_key_here" # 或设置环境变量 AGENTBAY_API_KEY +AGENTBAY_IMAGE_ID = "browser_latest" # 可选,默认为 browser_latest + +# 无头模式配置(agentbay 模式下也支持) +CDP_HEADLESS = False # 建议设为 False 以获得最佳反检测效果 +``` + +或者使用环境变量: +```bash +export AGENTBAY_API_KEY="your_api_key_here" +``` + +3. **启用配置**: +```python +ENABLE_CDP_MODE = True +ENABLE_WUYING_CDP_MODE = True +``` + ### 2. 运行测试 ```bash @@ -49,11 +90,19 @@ python main.py | 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | `ENABLE_CDP_MODE` | bool | False | 是否启用CDP模式 | -| `CDP_DEBUG_PORT` | int | 9222 | CDP调试端口 | -| `CDP_HEADLESS` | bool | False | CDP模式下的无头模式 | +| `ENABLE_WUYING_CDP_MODE` | bool | False | 是否使用 wuying-agentbay-sdk 模式 | +| `CDP_DEBUG_PORT` | int | 9222 | CDP调试端口(仅本地浏览器模式) | +| `CDP_HEADLESS` | bool | False | CDP模式下的无头模式(本地浏览器模式和 agentbay 模式均支持) | | `AUTO_CLOSE_BROWSER` | bool | True | 程序结束时是否关闭浏览器 | -### 高级配置 +### wuying-agentbay-sdk 配置 + +| 配置项 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| `AGENTBAY_API_KEY` | str | "" | wuying-agentbay-sdk 的 API Key | +| `AGENTBAY_IMAGE_ID` | str | "browser_latest" | wuying-agentbay-sdk 的镜像 ID | + +### 高级配置(仅本地浏览器模式) | 配置项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| @@ -137,7 +186,22 @@ python main.py ### 常见问题 -#### 1. 浏览器检测失败 +#### 1. wuying-agentbay-sdk 未安装 +**错误**: `wuying-agentbay-sdk 未安装,将使用本地浏览器模式` + +**解决方案**: +- 安装 wuying-agentbay-sdk: `pip install wuying-agentbay-sdk` +- 或者设置 `ENABLE_WUYING_CDP_MODE = False` 使用本地浏览器模式 + +#### 2. wuying-agentbay-sdk API Key 未设置 +**错误**: `使用 wuying-agentbay-sdk 需要设置 AGENTBAY_API_KEY 配置项` + +**解决方案**: +- 在配置文件中设置 `AGENTBAY_API_KEY = "your_api_key"` +- 或设置环境变量 `export AGENTBAY_API_KEY="your_api_key"` +- 或在 `.env` 文件中设置 `AGENTBAY_API_KEY=your_api_key` + +#### 3. 浏览器检测失败(仅本地浏览器模式) **错误**: `未找到可用的浏览器` **解决方案**: @@ -145,7 +209,7 @@ python main.py - 检查浏览器是否在标准路径下 - 使用`CUSTOM_BROWSER_PATH`指定浏览器路径 -#### 2. 端口被占用 +#### 4. 端口被占用(仅本地浏览器模式) **错误**: `无法找到可用的端口` **解决方案**: @@ -153,7 +217,7 @@ python main.py - 修改`CDP_DEBUG_PORT`为其他端口 - 系统会自动尝试下一个可用端口 -#### 3. 浏览器启动超时 +#### 5. 浏览器启动超时(仅本地浏览器模式) **错误**: `浏览器在30秒内未能启动` **解决方案**: @@ -161,7 +225,7 @@ python main.py - 检查系统资源是否充足 - 尝试关闭其他占用资源的程序 -#### 4. CDP连接失败 +#### 6. CDP连接失败 **错误**: `CDP连接失败` **解决方案**: @@ -198,7 +262,8 @@ ps aux | grep chrome ## 最佳实践 ### 1. 反检测优化 -- 保持`CDP_HEADLESS = False`以获得最佳反检测效果 +- 保持`CDP_HEADLESS = False`以获得最佳反检测效果(本地浏览器模式和 agentbay 模式均适用) +- 在 agentbay 模式下,headless 模式通过 `cmd_args` 参数控制,代码会自动处理 - 使用真实的User-Agent字符串 - 避免过于频繁的请求 @@ -221,16 +286,34 @@ ps aux | grep chrome CDP模式的工作原理: +### 本地浏览器模式 + 1. **浏览器检测**: 自动扫描系统中的Chrome/Edge安装路径 2. **进程启动**: 使用`--remote-debugging-port`参数启动浏览器 3. **CDP连接**: 通过WebSocket连接到浏览器的调试接口 4. **Playwright集成**: 使用`connectOverCDP`方法接管浏览器控制 5. **上下文管理**: 创建或复用浏览器上下文进行操作 +### wuying-agentbay-sdk 模式 + +1. **Session创建**: 通过 AgentBay SDK 创建远程浏览器 session +2. **浏览器初始化**: 使用 `BrowserOption` 配置浏览器参数(包括 headless 模式) +3. **CDP端点获取**: 从远程 session 获取 CDP WebSocket 端点 URL +4. **Playwright集成**: 使用`connectOverCDP`方法连接到远程浏览器 +5. **上下文管理**: 创建或复用浏览器上下文进行操作 +6. **资源清理**: 程序结束时自动删除远程 session + +**Headless 模式控制**: 在 agentbay 模式下,通过 `BrowserOption` 的 `cmd_args` 参数传递 `--headless=new` 来控制无头模式,代码会根据 `CDP_HEADLESS` 配置自动处理。 + 这种方式绕过了传统WebDriver的检测机制,提供了更加隐蔽的自动化能力。 ## 更新日志 +### v1.1.0 +- 新增 wuying-agentbay-sdk 支持 +- 支持使用远程浏览器 session +- 自动回退到本地浏览器模式(如果 SDK 未安装) + ### v1.0.0 - 初始版本发布 - 支持Windows和macOS的Chrome/Edge检测 diff --git a/MindSpider/DeepSentimentCrawling/MediaCrawler/tools/cdp_browser.py b/MindSpider/DeepSentimentCrawling/MediaCrawler/tools/cdp_browser.py index 3a2923745..72fc78e7f 100644 --- a/MindSpider/DeepSentimentCrawling/MediaCrawler/tools/cdp_browser.py +++ b/MindSpider/DeepSentimentCrawling/MediaCrawler/tools/cdp_browser.py @@ -14,23 +14,98 @@ import socket import httpx from typing import Optional, Dict, Any +from pathlib import Path +from pydantic_settings import BaseSettings +from pydantic import Field from playwright.async_api import Browser, BrowserContext, Playwright import config from tools.browser_launcher import BrowserLauncher from tools import utils +# 计算 .env 优先级:优先当前工作目录,其次项目根目录(BettaFish) +# 复用 MindSpider/config.py 的配置加载逻辑 +PROJECT_ROOT: Path = Path(__file__).resolve().parents[4] # 从 MediaCrawler/tools/cdp_browser.py 向上4级到 BettaFish +CWD_ENV: Path = Path.cwd() / ".env" +ENV_FILE: str = str(CWD_ENV if CWD_ENV.exists() else (PROJECT_ROOT / ".env")) + +class Settings(BaseSettings): + """AgentBay 配置,优先从环境变量和.env加载,复用 MindSpider 的统一配置逻辑""" + AGENTBAY_API_KEY: Optional[str] = Field(None, description="AgentBay API密钥,也可通过环境变量AGENTBAY_API_KEY设置") + AGENTBAY_IMAGE_ID: Optional[str] = Field("browser_latest", description="Wuying镜像ID,可通过环境变量AGENTBAY_IMAGE_ID设置") + + class Config: + env_file = ENV_FILE + env_prefix = "" + case_sensitive = False + extra = "allow" + +# 创建 settings 实例 +settings = Settings() + +# 注意:PyPI 安装的版本(0.12.0)只提供同步版本,没有异步版本 +# 所有调用都需要通过 asyncio.run_in_executor 适配到异步环境 + class CDPBrowserManager: """ CDP浏览器管理器,负责启动和管理通过CDP连接的浏览器 """ + # wuying-agentbay-sdk 相关类属性(所有实例共享) + # 缓存导入结果,None 表示未尝试过,True/False 表示导入成功/失败 + _agentbay_import_result: Optional[bool] = None + def __init__(self): self.launcher = BrowserLauncher() self.browser: Optional[Browser] = None self.browser_context: Optional[BrowserContext] = None self.debug_port: Optional[int] = None + # wuying-agentbay-sdk 相关实例属性 + self.agentbay: Optional[Any] = None + self.agentbay_session: Optional[Any] = None + + @classmethod + def _try_import_agentbay(cls): + """ + 尝试导入 agentbay SDK(同步版本) + + 注意:PyPI 安装的版本只提供同步版本(AgentBay),没有异步版本(AsyncAgentBay) + 所有同步调用都需要通过 asyncio.run_in_executor 适配到异步环境 + + BrowserOption 类在 agentbay 模块中定义(直接从 agentbay 导入) + + 无论成功还是失败,都只执行一次导入检查,后续调用直接返回缓存的结果。 + + Returns: + bool: 导入成功返回 True,失败返回 False + """ + # 如果已经尝试过导入,直接返回缓存的结果 + if cls._agentbay_import_result is not None: + return cls._agentbay_import_result + + # 第一次尝试导入 + try: + from agentbay import AgentBay, CreateSessionParams + cls._agentbay_import_result = True + return True + except ImportError as e: + cls._agentbay_import_result = False + # 检查是否已安装但导入失败,输出警告 + try: + import pkg_resources + dist = pkg_resources.get_distribution('wuying-agentbay-sdk') + error_msg = ( + f"[CDPBrowserManager] wuying-agentbay-sdk 已安装 (版本: {dist.version})," + f"但无法导入必要的类。错误: {e}。" + f"请检查 SDK 文档或尝试重新安装: " + f"pip uninstall -y wuying-agentbay-sdk && pip install wuying-agentbay-sdk" + ) + utils.logger.warning(error_msg) + except Exception: + # 包未安装,不输出警告(在需要时会提示) + pass + return False async def launch_and_connect( self, @@ -41,21 +116,216 @@ async def launch_and_connect( ) -> BrowserContext: """ 启动浏览器并通过CDP连接 + 支持两种模式: + 1. 使用 wuying-agentbay-sdk 创建 session 并连接 + 2. 使用本地浏览器通过 CDP 连接 """ try: - # 1. 检测浏览器路径 - browser_path = await self._get_browser_path() + # 检查是否使用 wuying-agentbay-sdk + if config.ENABLE_WUYING_CDP_MODE: + # 只有在配置使能时才尝试导入 SDK + if self.__class__._try_import_agentbay(): + utils.logger.info("[CDPBrowserManager] 使用 wuying-agentbay-sdk 模式") + return await self._launch_with_wuying(playwright, playwright_proxy, user_agent, headless) + else: + utils.logger.warning( + "[CDPBrowserManager] 配置了使用 wuying-agentbay-sdk 但 SDK 未安装," + "回退到本地浏览器模式" + ) - # 2. 获取可用端口 - self.debug_port = self.launcher.find_available_port(config.CDP_DEBUG_PORT) + utils.logger.info("[CDPBrowserManager] 使用本地浏览器模式") + return await self._launch_with_local_browser(playwright, playwright_proxy, user_agent, headless) - # 3. 启动浏览器 - await self._launch_browser(browser_path, headless) + except Exception as e: + utils.logger.error(f"[CDPBrowserManager] CDP浏览器启动失败: {e}") + await self.cleanup() + raise - # 4. 通过CDP连接 - await self._connect_via_cdp(playwright) + async def _launch_with_wuying( + self, + playwright: Playwright, + playwright_proxy: Optional[Dict] = None, + user_agent: Optional[str] = None, + headless: bool = False, + ) -> BrowserContext: + """ + 使用 wuying-agentbay-sdk 创建 session 并连接浏览器 + 支持异步和同步两种版本的 SDK + """ + try: + # 1. 初始化 AgentBay + # 从 settings 读取 API Key(BaseSettings 已自动从环境变量和 .env 文件加载) + api_key = settings.AGENTBAY_API_KEY + + if not api_key: + raise ValueError( + "使用 wuying-agentbay-sdk 需要设置 AGENTBAY_API_KEY 配置项," + "可在 .env 文件中设置 AGENTBAY_API_KEY 环境变量" + ) + + # 导入并使用 AgentBay(同步版本,通过 asyncio.run_in_executor 适配) + # 如果导入失败,会抛出 ImportError,由调用者处理 + from agentbay import AgentBay + self.agentbay = AgentBay(api_key=api_key) + utils.logger.info("[CDPBrowserManager] AgentBay (同步版本) 初始化成功") + + # 2. 创建 session + # 从 settings 读取镜像 ID(BaseSettings 已自动从环境变量和 .env 文件加载,默认值为 "browser_latest") + from agentbay import CreateSessionParams + image_id = settings.AGENTBAY_IMAGE_ID + params = CreateSessionParams(image_id=image_id) + + # 同步版本需要在线程池中运行 + loop = asyncio.get_event_loop() + result = await loop.run_in_executor(None, self.agentbay.create, params) + + self.agentbay_session = result.session + utils.logger.info(f"[CDPBrowserManager] Session 创建成功: {self.agentbay_session.session_id}") + + # 3. 初始化浏览器 + # 根据 SDK 实现,browser.initialize() 需要一个 option 参数 + # 通过检查方法签名和 SDK 模块来确定正确的 option 类型 + # 注意:BrowserOption 没有直接的 headless 参数,需要通过 cmd_args 传递命令行参数 + try: + # 检查是否有 initialize 方法 + if hasattr(self.agentbay_session.browser, 'initialize'): + # 创建 browser option + # 根据 headless 参数设置 cmd_args + # 如果 headless=True,传递 --headless=new;如果 headless=False,不传递 headless 参数(默认非 headless) + browser_option = None + try: + from agentbay import BrowserOption + cmd_args = [] + if headless: + # 启用 headless 模式 + cmd_args.append("--headless=new") + utils.logger.info(f"[CDPBrowserManager] 使用 BrowserOption 创建浏览器选项(headless 模式)") + else: + # 非 headless 模式,不传递 headless 参数(默认就是非 headless) + utils.logger.info(f"[CDPBrowserManager] 使用 BrowserOption 创建浏览器选项(非 headless 模式)") + + browser_option = BrowserOption(cmd_args=cmd_args if cmd_args else None) + except (ImportError, Exception) as e: + # BrowserOption 类不存在或创建失败,传递 None 让 SDK 创建默认实例 + utils.logger.debug(f"[CDPBrowserManager] 使用默认浏览器选项: {e}") + browser_option = None + + utils.logger.info(f"[CDPBrowserManager] 准备初始化浏览器,option: {browser_option}") + + # 同步版本需要在线程池中运行 + loop = asyncio.get_event_loop() + try: + success = await loop.run_in_executor( + None, + self.agentbay_session.browser.initialize, + browser_option + ) + except (TypeError, Exception) as e: + utils.logger.warning(f"[CDPBrowserManager] 浏览器初始化失败: {e}") + # 如果失败,尝试检查是否需要特定类型的对象 + import inspect + try: + sig = inspect.signature(self.agentbay_session.browser.initialize) + param_info = sig.parameters.get('option', None) + if param_info: + param_type = param_info.annotation + if param_type != inspect.Parameter.empty: + utils.logger.warning(f"[CDPBrowserManager] 需要的 option 类型: {param_type}") + except: + pass + success = False + if success: + utils.logger.info("[CDPBrowserManager] 浏览器初始化成功") + else: + utils.logger.warning("[CDPBrowserManager] 浏览器初始化返回 False,尝试继续获取 CDP URL") + else: + utils.logger.info("[CDPBrowserManager] 浏览器对象没有 initialize 方法,跳过初始化步骤") + except Exception as e: + utils.logger.warning(f"[CDPBrowserManager] 浏览器初始化异常: {e}") + # 继续执行,尝试获取 CDP URL - # 5. 创建浏览器上下文 + # 4. 获取 CDP 链接 + # 如果浏览器未初始化,尝试直接获取 CDP URL(某些 SDK 版本可能不需要初始化) + cdp_url = None + try: + # 同步版本需要在线程池中运行 + loop = asyncio.get_event_loop() + cdp_url = await loop.run_in_executor( + None, + self.agentbay_session.browser.get_endpoint_url + ) + utils.logger.info(f"[CDPBrowserManager] 获取到 CDP URL: {cdp_url[:100]}...") + except Exception as e: + error_msg = str(e).lower() + if "not initialized" in error_msg or "cannot access" in error_msg: + # 浏览器未初始化,需要先初始化 + utils.logger.error(f"[CDPBrowserManager] 无法获取 CDP URL,浏览器未初始化: {e}") + raise RuntimeError( + f"浏览器未初始化,无法获取 CDP URL。" + f"请检查 browser.initialize() 调用是否成功。" + f"错误: {e}" + ) + else: + raise + + # 5. 等待浏览器完全就绪(参考其他语言的实现,需要等待一段时间) + # 从测试代码看,通常需要等待 5 秒左右 + utils.logger.info("[CDPBrowserManager] 等待浏览器完全就绪...") + await asyncio.sleep(5) # 等待浏览器完全启动 + + # 6. 通过 CDP 连接,添加重试机制 + max_retries = 5 # 增加重试次数 + retry_delay = 3 # 初始延迟3秒 + last_error = None + + for attempt in range(max_retries): + try: + utils.logger.info(f"[CDPBrowserManager] 尝试连接 CDP (第 {attempt + 1}/{max_retries} 次)...") + # 使用 timeout 参数(30秒超时) + self.browser = await playwright.chromium.connect_over_cdp( + cdp_url, + timeout=30000 + ) + + if self.browser.is_connected(): + utils.logger.info("[CDPBrowserManager] 成功通过 wuying-agentbay-sdk 连接到浏览器") + break + else: + raise RuntimeError("CDP连接失败:浏览器未连接") + + except Exception as e: + last_error = e + error_msg = str(e).lower() + # 检查是否是可重试的错误 + retryable_keywords = ["ebadf", "connection", "timeout", "network", "websocket", "connect", "bad file"] + is_retryable = any(keyword in error_msg for keyword in retryable_keywords) + + if attempt < max_retries - 1 and is_retryable: + utils.logger.warning( + f"[CDPBrowserManager] CDP连接失败 (可重试): {str(e)[:200]}," + f"{retry_delay}秒后重试..." + ) + await asyncio.sleep(retry_delay) + retry_delay = min(retry_delay * 1.5, 10) # 指数退避,最多10秒 + else: + utils.logger.error(f"[CDPBrowserManager] CDP连接失败: {e}") + if attempt == max_retries - 1: + # 最后一次失败,提供更详细的错误信息 + raise RuntimeError( + f"CDP连接失败,已重试 {max_retries} 次。" + f"这可能是因为网络问题或浏览器未完全就绪。" + f"最后错误: {last_error}" + ) + raise + else: + # 所有重试都失败了 + raise RuntimeError( + f"CDP连接失败,已重试 {max_retries} 次。" + f"这可能是因为网络问题或浏览器未完全就绪。" + f"最后错误: {last_error}" + ) + + # 6. 创建或获取浏览器上下文 browser_context = await self._create_browser_context( playwright_proxy, user_agent ) @@ -64,10 +334,39 @@ async def launch_and_connect( return browser_context except Exception as e: - utils.logger.error(f"[CDPBrowserManager] CDP浏览器启动失败: {e}") - await self.cleanup() + utils.logger.error(f"[CDPBrowserManager] wuying-agentbay-sdk 模式启动失败: {e}") raise + async def _launch_with_local_browser( + self, + playwright: Playwright, + playwright_proxy: Optional[Dict] = None, + user_agent: Optional[str] = None, + headless: bool = False, + ) -> BrowserContext: + """ + 使用本地浏览器通过 CDP 连接 + """ + # 1. 检测浏览器路径 + browser_path = await self._get_browser_path() + + # 2. 获取可用端口 + self.debug_port = self.launcher.find_available_port(config.CDP_DEBUG_PORT) + + # 3. 启动浏览器 + await self._launch_browser(browser_path, headless) + + # 4. 通过CDP连接 + await self._connect_via_cdp(playwright) + + # 5. 创建浏览器上下文 + browser_context = await self._create_browser_context( + playwright_proxy, user_agent + ) + + self.browser_context = browser_context + return browser_context + async def _get_browser_path(self) -> str: """ 获取浏览器路径 @@ -314,13 +613,28 @@ async def cleanup(self): finally: self.browser = None - # 关闭浏览器进程(如果配置为自动关闭) - if config.AUTO_CLOSE_BROWSER: - self.launcher.cleanup() + # 如果使用 wuying-agentbay-sdk,需要删除 session + if self.agentbay_session: + try: + # 同步版本需要在线程池中运行 + loop = asyncio.get_event_loop() + await loop.run_in_executor(None, self.agentbay_session.delete) + utils.logger.info("[CDPBrowserManager] wuying session 已删除") + except Exception as wuying_error: + utils.logger.warning( + f"[CDPBrowserManager] 删除 wuying session 失败: {wuying_error}" + ) + finally: + self.agentbay_session = None + self.agentbay = None else: - utils.logger.info( - "[CDPBrowserManager] 浏览器进程保持运行(AUTO_CLOSE_BROWSER=False)" - ) + # 使用本地浏览器,需要关闭浏览器进程(如果配置为自动关闭) + if config.AUTO_CLOSE_BROWSER: + self.launcher.cleanup() + else: + utils.logger.info( + "[CDPBrowserManager] 浏览器进程保持运行(AUTO_CLOSE_BROWSER=False)" + ) except Exception as e: utils.logger.error(f"[CDPBrowserManager] 清理资源时出错: {e}") diff --git a/requirements.txt b/requirements.txt index 82c454d3a..6f00f91b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -85,3 +85,6 @@ flake8>=6.0.0 # ===== Web服务器 ===== fastapi==0.110.2 uvicorn==0.29.0 + +# ===== wuying-agentbay-sdk ===== +wuying-agentbay-sdk>=0.12.0