Преглед изворни кода

feat(cloud-browser): 添加云浏览器模式支持及相关示例文档

- 在 baseClass.py 中添加 use_cloud 参数支持云浏览器模式
- 创建云浏览器示例文件 cloud_browser_example.py 包含7个完整示例
- 添加详细的使用指南文档:README_CLOUD_BROWSER.md、CLOUD_BROWSER_GUIDE.md
- 添加登录处理指南文档:LOGIN_HANDLING_GUIDE.md、LOGIN_UPDATE_SUMMARY.md
- 更新 .gitignore、requirements.txt 和 Claude 配置文件
max_liu пре 1 месец
родитељ
комит
63c33fcf6d

+ 2 - 1
.claude/settings.local.json

@@ -6,7 +6,8 @@
       "Bash(pip show:*)",
       "Read(//usr/local/anaconda3/lib/python3.13/site-packages/browser_use/**)",
       "Bash(tee:*)",
-      "Bash(browser-use:*)"
+      "Bash(browser-use:*)",
+      "Bash(pip install:*)"
     ],
     "deny": [],
     "ask": []

+ 1 - 1
.gitignore

@@ -48,7 +48,7 @@ htmlcov/
 .DS_Store
 Thumbs.db
 
-.env
+# .env
 debug.log
 info.log
 .browser_use_files

+ 21 - 13
agent/tools/builtin/baseClass.py

@@ -49,6 +49,7 @@ async def init_browser_session(
     user_data_dir: Optional[str] = None,
     profile_name: str = "default",
     browser_profile: Optional[BrowserProfile] = None,
+    use_cloud: bool = False,
     **kwargs
 ) -> tuple[BrowserSession, Tools]:
     """
@@ -59,6 +60,7 @@ async def init_browser_session(
         user_data_dir: 用户数据目录(用于保存登录状态)
         profile_name: 配置文件名称
         browser_profile: BrowserProfile 对象(用于预设 cookies 等)
+        use_cloud: 是否使用云浏览器(默认 False,使用本地浏览器)
         **kwargs: 其他 BrowserSession 参数
 
     Returns:
@@ -70,27 +72,33 @@ async def init_browser_session(
         return _browser_session, _browser_tools
 
     # 设置用户数据目录(持久化登录状态)
-    if user_data_dir is None and profile_name:
+    if user_data_dir is None and profile_name and not use_cloud:
         user_data_dir = str(Path.home() / ".browser_use" / "profiles" / profile_name)
         Path(user_data_dir).mkdir(parents=True, exist_ok=True)
 
     # 创建浏览器会话
-    # 明确指定 is_local=True 以确保本地浏览器启动
     session_params = {
         "headless": headless,
-        "is_local": True,  # 明确指定本地浏览器
     }
 
-    # macOS 上显式指定 Chrome 路径
-    import platform
-    if platform.system() == "Darwin":  # macOS
-        chrome_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
-        if Path(chrome_path).exists():
-            session_params["executable_path"] = chrome_path
-
-    # 只在有值时才添加 user_data_dir
-    if user_data_dir:
-        session_params["user_data_dir"] = user_data_dir
+    if use_cloud:
+        # 云浏览器模式
+        session_params["use_cloud"] = True
+        print("🌐 使用云浏览器模式")
+    else:
+        # 本地浏览器模式
+        session_params["is_local"] = True
+
+        # macOS 上显式指定 Chrome 路径
+        import platform
+        if platform.system() == "Darwin":  # macOS
+            chrome_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
+            if Path(chrome_path).exists():
+                session_params["executable_path"] = chrome_path
+
+        # 只在有值时才添加 user_data_dir
+        if user_data_dir:
+            session_params["user_data_dir"] = user_data_dir
 
     # 只在有值时才添加 browser_profile
     if browser_profile:

+ 605 - 0
examples/CLOUD_BROWSER_GUIDE.md

@@ -0,0 +1,605 @@
+# Browser-Use 云浏览器模式使用指南
+
+## 目录
+- [简介](#简介)
+- [云浏览器 vs 本地浏览器](#云浏览器-vs-本地浏览器)
+- [环境配置](#环境配置)
+- [快速开始](#快速开始)
+- [核心概念](#核心概念)
+- [示例代码](#示例代码)
+- [高级用法](#高级用法)
+- [常见问题](#常见问题)
+- [最佳实践](#最佳实践)
+
+---
+
+## 简介
+
+Browser-Use 云浏览器模式允许你在云端运行浏览器自动化任务,无需在本地安装 Chrome/Chromium。这对于以下场景特别有用:
+
+- 🚀 **无头服务器部署** - 在没有图形界面的服务器上运行
+- 🌍 **分布式爬虫** - 轻松扩展到多个云浏览器实例
+- 💻 **跨平台一致性** - 避免本地环境差异
+- 🔒 **安全隔离** - 浏览器运行在隔离的云环境中
+- 📊 **资源优化** - 不占用本地计算资源
+
+---
+
+## 云浏览器 vs 本地浏览器
+
+| 特性 | 云浏览器 | 本地浏览器 |
+|------|---------|-----------|
+| **安装要求** | 无需安装 Chrome | 需要安装 Chrome/Chromium |
+| **运行环境** | 云端 | 本地机器 |
+| **资源占用** | 不占用本地资源 | 占用本地 CPU/内存 |
+| **网络延迟** | 可能有轻微延迟 | 无网络延迟 |
+| **成本** | 需要 API 配额 | 免费 |
+| **调试** | 提供 Live URL 实时查看 | 可以直接看到浏览器窗口 |
+| **适用场景** | 服务器部署、分布式任务 | 本地开发、调试 |
+
+---
+
+## 环境配置
+
+### 1. 安装依赖
+
+```bash
+# 安装 browser-use
+pip install browser-use
+
+# 安装云浏览器所需的额外依赖
+pip install python-socks
+```
+
+### 2. 获取 API Key
+
+1. 访问 [Browser-Use 官网](https://browser-use.com)
+2. 注册账号并获取 API Key
+3. 将 API Key 添加到 `.env` 文件
+
+### 3. 配置环境变量
+
+在项目根目录的 `.env` 文件中添加:
+
+```bash
+# Browser-Use 云浏览器 API Key
+BROWSER_USE_API_KEY=your_api_key_here
+
+# 可选:如果需要使用 LLM 功能
+GOOGLE_API_KEY=your_google_api_key
+GEMINI_API_KEY=your_gemini_api_key
+```
+
+---
+
+## 快速开始
+
+### 最简单的云浏览器示例
+
+```python
+import asyncio
+import os
+from dotenv import load_dotenv
+from agent.tools.builtin.baseClass import (
+    init_browser_session,
+    cleanup_browser_session,
+    navigate_to_url,
+)
+
+# 加载环境变量
+load_dotenv()
+
+async def main():
+    # 初始化云浏览器(关键:use_cloud=True)
+    browser, tools = await init_browser_session(
+        headless=True,
+        use_cloud=True,  # 启用云浏览器
+    )
+
+    print("✅ 云浏览器已启动")
+
+    # 访问网页
+    result = await navigate_to_url("https://www.baidu.com")
+    print(f"导航结果: {result.title}")
+
+    # 清理
+    await cleanup_browser_session()
+    print("🧹 浏览器已关闭")
+
+if __name__ == "__main__":
+    asyncio.run(main())
+```
+
+### 运行示例
+
+```bash
+# 运行默认示例(示例 1)
+python examples/cloud_browser_example.py
+
+# 运行指定示例
+python examples/cloud_browser_example.py --example 2
+
+# 运行所有示例
+python examples/cloud_browser_example.py --all
+```
+
+---
+
+## 核心概念
+
+### 1. 初始化云浏览器会话
+
+```python
+from agent.tools.builtin.baseClass import init_browser_session
+
+# 云浏览器模式
+browser, tools = await init_browser_session(
+    headless=True,      # 云浏览器通常使用无头模式
+    use_cloud=True,     # 关键参数:启用云浏览器
+)
+```
+
+**参数说明:**
+- `headless`: 是否使用无头模式(云浏览器推荐 True)
+- `use_cloud`: 是否使用云浏览器(True=云浏览器,False=本地浏览器)
+- `browser_profile`: 可选,预设 cookies、localStorage 等
+- `**kwargs`: 其他 BrowserSession 参数
+
+### 2. 使用 BrowserProfile 预设配置
+
+```python
+from browser_use import BrowserProfile
+
+# 创建配置文件
+profile = BrowserProfile(
+    # 预设 cookies
+    cookies=[
+        {
+            "name": "session_id",
+            "value": "abc123",
+            "domain": ".example.com",
+            "path": "/",
+        }
+    ],
+    # 预设 localStorage
+    local_storage={
+        "example.com": {
+            "token": "your_token",
+            "user_id": "12345",
+        }
+    },
+    # 自定义 User-Agent
+    user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
+)
+
+# 使用配置初始化浏览器
+browser, tools = await init_browser_session(
+    use_cloud=True,
+    browser_profile=profile,
+)
+```
+
+### 3. 可用的工具函数
+
+项目提供了丰富的浏览器操作工具,所有工具都支持云浏览器:
+
+#### 导航类工具
+```python
+# 导航到 URL
+await navigate_to_url("https://example.com")
+
+# 在新标签页打开
+await navigate_to_url("https://example.com", new_tab=True)
+
+# 搜索
+await search_web("Python async", engine="google")
+
+# 返回上一页
+await go_back()
+
+# 等待
+await wait(seconds=3)
+```
+
+#### 元素交互工具
+```python
+# 点击元素(需要先获取元素索引)
+await click_element(index=5)
+
+# 输入文本
+await input_text(index=0, text="Hello World", clear=True)
+
+# 发送按键
+await send_keys("Enter")
+await send_keys("Control+A")
+
+# 上传文件
+await upload_file(index=7, path="/path/to/file.pdf")
+```
+
+#### 页面操作工具
+```python
+# 滚动页面
+await scroll_page(down=True, pages=2.0)
+
+# 查找文本
+await find_text("Privacy Policy")
+
+# 截图
+await screenshot()
+
+# 获取页面 HTML
+html_result = await get_page_html()
+
+# 获取可交互元素
+selector_result = await get_selector_map()
+
+# 执行 JavaScript
+result = await evaluate("document.title")
+```
+
+#### 标签页管理
+```python
+# 切换标签页
+await switch_tab(tab_id="a3f2")
+
+# 关闭标签页
+await close_tab(tab_id="a3f2")
+```
+
+#### 文件操作
+```python
+# 写入文件
+await write_file("output.txt", "Hello World")
+
+# 读取文件
+content = await read_file("input.txt")
+
+# 替换文件内容
+await replace_file("config.txt", "old_value", "new_value")
+```
+
+---
+
+## 示例代码
+
+### 示例 1: 基础导航操作
+
+```python
+async def example_basic_navigation():
+    """访问网页并获取页面信息"""
+    browser, tools = await init_browser_session(use_cloud=True)
+
+    # 导航到百度
+    await navigate_to_url("https://www.baidu.com")
+    await wait(2)
+
+    # 获取页面标题
+    title_result = await evaluate("document.title")
+    print(f"页面标题: {title_result.output}")
+
+    # 截图
+    await screenshot()
+
+    await cleanup_browser_session()
+```
+
+### 示例 2: 搜索和内容提取
+
+```python
+async def example_search():
+    """使用搜索引擎并提取内容"""
+    browser, tools = await init_browser_session(use_cloud=True)
+
+    # 搜索
+    await search_web("Python async programming", engine="google")
+    await wait(3)
+
+    # 获取页面 HTML
+    html_result = await get_page_html()
+    print(f"HTML 长度: {len(html_result.metadata.get('html', ''))} 字符")
+
+    # 获取可交互元素
+    selector_result = await get_selector_map()
+    print(selector_result.output)
+
+    await cleanup_browser_session()
+```
+
+### 示例 3: 使用 BrowserProfile
+
+```python
+async def example_with_profile():
+    """使用预设配置"""
+    from browser_use import BrowserProfile
+
+    # 创建配置
+    profile = BrowserProfile(
+        cookies=[{
+            "name": "test_cookie",
+            "value": "test_value",
+            "domain": ".example.com",
+            "path": "/",
+        }],
+        user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
+    )
+
+    # 使用配置初始化
+    browser, tools = await init_browser_session(
+        use_cloud=True,
+        browser_profile=profile,
+    )
+
+    # 访问网页
+    await navigate_to_url("https://httpbin.org/headers")
+    await wait(2)
+
+    # 检查 User-Agent
+    ua_result = await evaluate("navigator.userAgent")
+    print(f"User-Agent: {ua_result.output}")
+
+    await cleanup_browser_session()
+```
+
+### 示例 4: 表单交互
+
+```python
+async def example_form_interaction():
+    """填写表单"""
+    browser, tools = await init_browser_session(use_cloud=True)
+
+    # 访问表单页面
+    await navigate_to_url("https://httpbin.org/forms/post")
+    await wait(2)
+
+    # 获取页面元素
+    selector_result = await get_selector_map()
+    print(f"找到 {selector_result.long_term_memory}")
+
+    # 根据实际页面结构填写表单
+    # await input_text(index=0, text="用户名")
+    # await input_text(index=1, text="密码")
+    # await click_element(index=2)  # 提交按钮
+
+    await cleanup_browser_session()
+```
+
+### 示例 5: 多标签页操作
+
+```python
+async def example_multi_tab():
+    """管理多个标签页"""
+    browser, tools = await init_browser_session(use_cloud=True)
+
+    # 第一个标签页
+    await navigate_to_url("https://www.baidu.com")
+    await wait(2)
+
+    # 新标签页
+    await navigate_to_url("https://www.google.com", new_tab=True)
+    await wait(2)
+
+    # 获取当前页面信息
+    title_result = await evaluate("document.title")
+    print(f"当前标题: {title_result.output}")
+
+    await cleanup_browser_session()
+```
+
+---
+
+## 高级用法
+
+### 1. 实时查看云浏览器
+
+云浏览器启动时会输出一个 Live URL,你可以在浏览器中打开这个 URL 实时查看云浏览器的操作:
+
+```
+INFO [cloud] 🔗 Live URL: https://live.browser-use.com?wss=https%3A%2F%2F...
+```
+
+复制这个 URL 到浏览器中打开,即可实时查看云浏览器的操作。
+
+### 2. 错误处理
+
+```python
+async def example_with_error_handling():
+    browser = None
+    try:
+        browser, tools = await init_browser_session(use_cloud=True)
+
+        result = await navigate_to_url("https://example.com")
+        if result.error:
+            print(f"导航失败: {result.error}")
+            return
+
+        # 其他操作...
+
+    except Exception as e:
+        print(f"发生错误: {str(e)}")
+    finally:
+        if browser:
+            await cleanup_browser_session()
+```
+
+### 3. 会话复用
+
+```python
+# 全局会话会自动复用
+# 第一次调用会创建新会话
+browser1, tools1 = await init_browser_session(use_cloud=True)
+
+# 后续调用会返回同一个会话
+browser2, tools2 = await init_browser_session(use_cloud=True)
+
+# browser1 和 browser2 是同一个对象
+assert browser1 is browser2
+```
+
+### 4. 强制终止浏览器
+
+```python
+from agent.tools.builtin.baseClass import kill_browser_session
+
+# 优雅关闭(推荐)
+await cleanup_browser_session()
+
+# 强制终止(用于异常情况)
+await kill_browser_session()
+```
+
+---
+
+## 常见问题
+
+### Q1: 云浏览器启动失败
+
+**问题:** `python-socks is required to use a SOCKS proxy`
+
+**解决:**
+```bash
+pip install python-socks
+```
+
+### Q2: API Key 无效
+
+**问题:** `未找到 BROWSER_USE_API_KEY`
+
+**解决:**
+1. 确保 `.env` 文件在项目根目录
+2. 确保 API Key 格式正确
+3. 确保代码中调用了 `load_dotenv()`
+
+### Q3: 云浏览器连接超时
+
+**问题:** 云浏览器启动后无法连接
+
+**解决:**
+1. 检查网络连接
+2. 检查防火墙设置
+3. 尝试使用代理
+
+### Q4: 如何切换回本地浏览器
+
+**解决:**
+```python
+# 使用本地浏览器
+browser, tools = await init_browser_session(
+    use_cloud=False,  # 或者不传这个参数,默认是 False
+)
+```
+
+### Q5: 云浏览器的配额限制
+
+**问题:** API 配额用完了怎么办
+
+**解决:**
+1. 查看 Browser-Use 官网的定价计划
+2. 升级到更高的配额
+3. 优化代码,减少不必要的浏览器操作
+
+---
+
+## 最佳实践
+
+### 1. 合理使用 wait
+
+```python
+# ❌ 不好:固定等待时间太长
+await wait(10)
+
+# ✅ 好:根据实际需要调整等待时间
+await wait(2)  # 页面加载
+await wait(1)  # 动画完成
+```
+
+### 2. 及时清理会话
+
+```python
+# ✅ 使用 try-finally 确保清理
+try:
+    browser, tools = await init_browser_session(use_cloud=True)
+    # 操作...
+finally:
+    await cleanup_browser_session()
+```
+
+### 3. 使用 BrowserProfile 避免重复登录
+
+```python
+# ✅ 预设 cookies,避免每次都登录
+profile = BrowserProfile(
+    cookies=[
+        # 从之前的会话中保存的 cookies
+    ]
+)
+browser, tools = await init_browser_session(
+    use_cloud=True,
+    browser_profile=profile,
+)
+```
+
+### 4. 批量操作时复用会话
+
+```python
+# ✅ 一次会话处理多个任务
+browser, tools = await init_browser_session(use_cloud=True)
+
+for url in urls:
+    await navigate_to_url(url)
+    # 处理页面...
+
+await cleanup_browser_session()
+```
+
+### 5. 使用 Live URL 调试
+
+```python
+# 开发时启用 Live URL 查看
+# 云浏览器启动时会自动输出 Live URL
+# 复制到浏览器中打开即可实时查看
+```
+
+---
+
+## 性能优化建议
+
+1. **减少不必要的等待**
+   - 使用最小必要的等待时间
+   - 避免固定的长时间等待
+
+2. **批量处理**
+   - 在一个会话中处理多个任务
+   - 避免频繁创建/销毁会话
+
+3. **合理使用截图**
+   - 只在必要时截图
+   - 截图会增加网络传输时间
+
+4. **优化元素定位**
+   - 使用 `get_selector_map` 一次性获取所有元素
+   - 避免重复查询相同元素
+
+---
+
+## 技术支持
+
+- **Browser-Use 官方文档**: https://docs.browser-use.com
+- **GitHub Issues**: https://github.com/browser-use/browser-use
+- **项目内部文档**: 查看 `agent/tools/builtin/baseClass.py` 的注释
+
+---
+
+## 更新日志
+
+### v1.0.0 (2026-01-30)
+- ✅ 初始版本
+- ✅ 支持云浏览器模式
+- ✅ 提供 5 个完整示例
+- ✅ 完整的使用文档
+
+---
+
+## 许可证
+
+本项目遵循项目主许可证。

+ 875 - 0
examples/LOGIN_HANDLING_GUIDE.md

@@ -0,0 +1,875 @@
+# Browser-Use 登录处理完整指南
+
+## 目录
+- [概述](#概述)
+- [登录场景分类](#登录场景分类)
+- [三种登录处理方式](#三种登录处理方式)
+- [方式1: 手动登录(推荐)](#方式1-手动登录推荐)
+- [方式2: Cookie复用](#方式2-cookie复用)
+- [方式3: 自动化登录](#方式3-自动化登录)
+- [完整实战示例](#完整实战示例)
+- [登录状态检测](#登录状态检测)
+- [Cookie管理最佳实践](#cookie管理最佳实践)
+- [常见问题](#常见问题)
+- [安全建议](#安全建议)
+
+---
+
+## 概述
+
+在使用 Browser-Use 进行网页自动化时,很多网站需要登录才能访问完整内容。本文档详细介绍如何在云浏览器模式下处理各种登录场景。
+
+### 为什么需要登录处理?
+
+- 🔒 **内容保护** - 很多网站的核心内容需要登录才能访问
+- 🚫 **反爬虫** - 未登录用户可能被限流或拦截
+- 📊 **个性化数据** - 某些数据只对登录用户可见
+- 🎯 **操作权限** - 发布、评论等操作需要登录
+
+---
+
+## 登录场景分类
+
+### 1. 简单账号密码登录
+- 输入用户名/邮箱
+- 输入密码
+- 点击登录按钮
+- **示例**: GitHub, Twitter
+
+### 2. 扫码登录
+- 显示二维码
+- 用户扫码确认
+- **示例**: 微信, 小红书, 淘宝
+
+### 3. 验证码登录
+- 输入手机号
+- 接收验证码
+- 输入验证码
+- **示例**: 大部分国内网站
+
+### 4. 第三方登录
+- OAuth 授权
+- 跳转到第三方平台
+- **示例**: Google登录, Facebook登录
+
+### 5. 多因素认证 (MFA)
+- 密码 + 验证码
+- 密码 + 邮箱确认
+- **示例**: 银行网站, 企业系统
+
+---
+
+## 三种登录处理方式
+
+| 方式 | 适用场景 | 优点 | 缺点 |
+|------|---------|------|------|
+| **手动登录** | 扫码、验证码、复杂登录 | 最灵活,成功率高 | 需要人工介入 |
+| **Cookie复用** | 频繁使用同一账号 | 快速,无需重复登录 | Cookie会过期 |
+| **自动化登录** | 简单账号密码登录 | 完全自动化 | 容易被反爬虫检测 |
+
+---
+
+## 方式1: 手动登录(推荐)
+
+### 核心思路
+
+1. 启动云浏览器(非无头模式)
+2. 导航到登录页面
+3. 使用 `wait_for_user_action` 暂停自动化
+4. 用户在 Live URL 中手动完成登录
+5. 用户按 Enter 继续自动化流程
+
+### 完整代码示例
+
+```python
+import asyncio
+from dotenv import load_dotenv
+from agent.tools.builtin.baseClass import (
+    init_browser_session,
+    cleanup_browser_session,
+    navigate_to_url,
+    wait,
+    wait_for_user_action,
+    evaluate,
+)
+
+load_dotenv()
+
+async def manual_login_example():
+    """
+    手动登录示例 - 适用于所有登录场景
+    """
+    try:
+        # 步骤 1: 初始化云浏览器(非无头模式)
+        print("🌐 启动云浏览器...")
+        browser, tools = await init_browser_session(
+            headless=False,  # 关键:设置为 False
+            use_cloud=True,
+        )
+
+        print("✅ 云浏览器已启动")
+        print("📝 提示: 查找日志中的 '🔗 Live URL',在浏览器中打开该链接")
+
+        # 步骤 2: 导航到目标网站
+        print("\n📍 导航到小红书...")
+        await navigate_to_url("https://www.xiaohongshu.com")
+        await wait(3)
+
+        # 步骤 3: 检查登录状态(可选)
+        print("\n🔍 检查登录状态...")
+        check_login_js = """
+        (function() {
+            // 检查是否有用户头像或用户名
+            const userAvatar = document.querySelector('[class*="avatar"]');
+            const userName = document.querySelector('[class*="username"]');
+            const loginBtn = document.querySelector('[class*="login"]');
+
+            return {
+                isLoggedIn: !!(userAvatar || userName),
+                hasLoginBtn: !!loginBtn
+            };
+        })()
+        """
+
+        status = await evaluate(check_login_js)
+        print(f"   登录状态: {status.output}")
+
+        # 步骤 4: 等待用户手动登录
+        print("\n👤 等待用户登录...")
+        print("=" * 60)
+        print("请按以下步骤操作:")
+        print("1. 在日志中找到 '🔗 Live URL'")
+        print("2. 复制该 URL 并在浏览器中打开")
+        print("3. 在 Live URL 页面中完成登录(扫码或账号密码)")
+        print("4. 登录成功后,回到这里按 Enter 继续")
+        print("=" * 60)
+
+        await wait_for_user_action(
+            message="请在云浏览器中完成登录,完成后按 Enter 继续",
+            timeout=300  # 5 分钟超时
+        )
+
+        print("\n✅ 用户已确认登录完成")
+
+        # 步骤 5: 验证登录状态
+        print("\n🔍 验证登录状态...")
+        status = await evaluate(check_login_js)
+        print(f"   登录状态: {status.output}")
+
+        # 步骤 6: 继续后续操作
+        print("\n🎯 开始执行后续任务...")
+        # 这里可以继续你的自动化任务
+        # 例如:搜索、爬取数据等
+
+        print("\n✅ 任务完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+
+if __name__ == "__main__":
+    asyncio.run(manual_login_example())
+```
+
+### 关键点说明
+
+1. **headless=False**: 必须设置为 False,否则无法在 Live URL 中看到页面
+2. **Live URL**: 云浏览器启动时会输出,格式如 `https://live.browser-use.com?wss=...`
+3. **wait_for_user_action**: 暂停自动化,等待用户操作
+4. **timeout**: 设置合理的超时时间(建议 3-5 分钟)
+
+---
+
+## 方式2: Cookie复用
+
+### 核心思路
+
+1. 第一次手动登录并保存 Cookie
+2. 后续使用 BrowserProfile 预设 Cookie
+3. 跳过登录步骤,直接访问内容
+
+### 步骤 1: 首次登录并保存 Cookie
+
+```python
+import asyncio
+import json
+from pathlib import Path
+from dotenv import load_dotenv
+from agent.tools.builtin.baseClass import (
+    init_browser_session,
+    cleanup_browser_session,
+    navigate_to_url,
+    wait,
+    wait_for_user_action,
+    evaluate,
+)
+
+load_dotenv()
+
+async def save_cookies_after_login():
+    """
+    首次登录并保存 Cookie
+    """
+    try:
+        # 初始化浏览器
+        browser, tools = await init_browser_session(
+            headless=False,
+            use_cloud=True,
+        )
+
+        print("✅ 云浏览器已启动")
+
+        # 导航到网站
+        await navigate_to_url("https://www.xiaohongshu.com")
+        await wait(3)
+
+        # 等待用户登录
+        print("\n👤 请在 Live URL 中完成登录...")
+        await wait_for_user_action(
+            message="登录完成后按 Enter 继续",
+            timeout=300
+        )
+
+        # 获取 Cookie
+        print("\n💾 保存 Cookie...")
+        get_cookies_js = """
+        (function() {
+            return document.cookie;
+        })()
+        """
+
+        cookies_result = await evaluate(get_cookies_js)
+        cookies_str = cookies_result.output
+
+        # 解析 Cookie 字符串为列表
+        cookies = []
+        for cookie_str in cookies_str.split('; '):
+            if '=' in cookie_str:
+                name, value = cookie_str.split('=', 1)
+                cookies.append({
+                    "name": name,
+                    "value": value,
+                    "domain": ".xiaohongshu.com",
+                    "path": "/",
+                })
+
+        # 保存到文件
+        cookie_file = Path("cookies_xhs.json")
+        with open(cookie_file, "w", encoding="utf-8") as f:
+            json.dump(cookies, f, ensure_ascii=False, indent=2)
+
+        print(f"✅ Cookie 已保存到: {cookie_file}")
+        print(f"   共 {len(cookies)} 个 Cookie")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+
+if __name__ == "__main__":
+    asyncio.run(save_cookies_after_login())
+```
+
+### 步骤 2: 使用保存的 Cookie
+
+```python
+import asyncio
+import json
+from pathlib import Path
+from dotenv import load_dotenv
+from browser_use import BrowserProfile
+from agent.tools.builtin.baseClass import (
+    init_browser_session,
+    cleanup_browser_session,
+    navigate_to_url,
+    wait,
+    evaluate,
+)
+
+load_dotenv()
+
+async def use_saved_cookies():
+    """
+    使用保存的 Cookie 跳过登录
+    """
+    try:
+        # 读取保存的 Cookie
+        cookie_file = Path("cookies_xhs.json")
+        if not cookie_file.exists():
+            print("❌ Cookie 文件不存在,请先运行 save_cookies_after_login()")
+            return
+
+        with open(cookie_file, "r", encoding="utf-8") as f:
+            cookies = json.load(f)
+
+        print(f"✅ 加载了 {len(cookies)} 个 Cookie")
+
+        # 创建 BrowserProfile 并预设 Cookie
+        profile = BrowserProfile(
+            cookies=cookies,
+            user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
+        )
+
+        # 初始化浏览器(使用 profile)
+        browser, tools = await init_browser_session(
+            headless=True,  # 可以使用无头模式
+            use_cloud=True,
+            browser_profile=profile,  # 传入 profile
+        )
+
+        print("✅ 云浏览器已启动(带 Cookie)")
+
+        # 直接访问需要登录的页面
+        print("\n📍 访问小红书(应该已登录)...")
+        await navigate_to_url("https://www.xiaohongshu.com")
+        await wait(3)
+
+        # 验证登录状态
+        print("\n🔍 验证登录状态...")
+        check_login_js = """
+        (function() {
+            const userAvatar = document.querySelector('[class*="avatar"]');
+            const userName = document.querySelector('[class*="username"]');
+            return {
+                isLoggedIn: !!(userAvatar || userName)
+            };
+        })()
+        """
+
+        status = await evaluate(check_login_js)
+        print(f"   登录状态: {status.output}")
+
+        # 继续后续操作
+        print("\n🎯 开始执行任务...")
+        # 你的自动化任务...
+
+        print("\n✅ 任务完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+
+if __name__ == "__main__":
+    asyncio.run(use_saved_cookies())
+```
+
+### Cookie 复用的注意事项
+
+1. **Cookie 过期**: Cookie 有有效期,过期后需要重新登录
+2. **安全性**: Cookie 文件包含敏感信息,不要提交到 Git
+3. **域名匹配**: Cookie 的 domain 必须正确设置
+4. **定期更新**: 建议定期重新获取 Cookie
+
+---
+
+## 方式3: 自动化登录
+
+### 适用场景
+
+- 简单的账号密码登录
+- 没有验证码或反爬虫检测
+- 测试环境或内部系统
+
+### 示例代码
+
+```python
+import asyncio
+from dotenv import load_dotenv
+from agent.tools.builtin.baseClass import (
+    init_browser_session,
+    cleanup_browser_session,
+    navigate_to_url,
+    wait,
+    get_selector_map,
+    input_text,
+    click_element,
+    evaluate,
+)
+
+load_dotenv()
+
+async def automated_login_example():
+    """
+    自动化登录示例(仅适用于简单场景)
+    """
+    try:
+        # 初始化浏览器
+        browser, tools = await init_browser_session(
+            headless=True,
+            use_cloud=True,
+        )
+
+        print("✅ 云浏览器已启动")
+
+        # 导航到登录页面
+        print("\n📍 导航到登录页面...")
+        await navigate_to_url("https://example.com/login")
+        await wait(2)
+
+        # 获取页面元素
+        print("\n🎯 获取页面元素...")
+        selector_result = await get_selector_map()
+        print(f"   找到 {selector_result.long_term_memory}")
+
+        # 注意:需要根据实际页面找到正确的元素索引
+        # 这里假设:
+        # - 索引 0 是用户名输入框
+        # - 索引 1 是密码输入框
+        # - 索引 2 是登录按钮
+
+        # 输入用户名
+        print("\n📝 输入用户名...")
+        await input_text(index=0, text="your_username", clear=True)
+        await wait(1)
+
+        # 输入密码
+        print("\n🔑 输入密码...")
+        await input_text(index=1, text="your_password", clear=True)
+        await wait(1)
+
+        # 点击登录按钮
+        print("\n🖱️ 点击登录按钮...")
+        await click_element(index=2)
+        await wait(3)
+
+        # 验证登录状态
+        print("\n🔍 验证登录状态...")
+        check_login_js = """
+        (function() {
+            // 检查是否跳转到首页或有用户信息
+            const currentUrl = window.location.href;
+            const userInfo = document.querySelector('[class*="user"]');
+            return {
+                currentUrl: currentUrl,
+                isLoggedIn: !!userInfo
+            };
+        })()
+        """
+
+        status = await evaluate(check_login_js)
+        print(f"   登录状态: {status.output}")
+
+        print("\n✅ 登录完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+
+if __name__ == "__main__":
+    asyncio.run(automated_login_example())
+```
+
+### 自动化登录的风险
+
+⚠️ **警告**: 自动化登录容易被检测,可能导致:
+- 账号被封禁
+- IP 被拉黑
+- 触发验证码
+- 登录失败
+
+**建议**: 优先使用手动登录或 Cookie 复用。
+
+---
+
+## 完整实战示例
+
+### 小红书搜索(带登录处理)
+
+这是一个完整的实战示例,展示了如何处理小红书的登录并进行搜索。
+
+```python
+# 完整代码见 examples/cloud_browser_example.py 中的 example_6_xhs_search_save()
+```
+
+**执行流程**:
+
+1. ✅ 启动云浏览器(非无头模式)
+2. 📍 访问小红书首页
+3. 🔍 检查登录状态
+4. 👤 等待用户手动登录(通过 Live URL)
+5. ✅ 用户确认登录完成
+6. 🔍 执行搜索
+7. 📜 滚动加载更多内容
+8. 📊 提取搜索结果
+9. 💾 保存到 JSON 文件
+
+**运行方式**:
+
+```bash
+python examples/cloud_browser_example.py --example 6
+```
+
+---
+
+## 登录状态检测
+
+### 通用检测方法
+
+```javascript
+// 方法 1: 检查用户相关元素
+(function() {
+    const userAvatar = document.querySelector('[class*="avatar"]');
+    const userName = document.querySelector('[class*="username"]');
+    const userMenu = document.querySelector('[class*="user-menu"]');
+    const loginBtn = document.querySelector('[class*="login"]');
+
+    return {
+        isLoggedIn: !!(userAvatar || userName || userMenu),
+        hasLoginBtn: !!loginBtn
+    };
+})()
+
+// 方法 2: 检查 Cookie
+(function() {
+    const cookies = document.cookie;
+    const hasSessionCookie = cookies.includes('session') ||
+                            cookies.includes('token') ||
+                            cookies.includes('auth');
+    return {
+        hasCookie: hasSessionCookie,
+        cookieCount: cookies.split(';').length
+    };
+})()
+
+// 方法 3: 检查 LocalStorage
+(function() {
+    const hasToken = !!localStorage.getItem('token') ||
+                    !!localStorage.getItem('user') ||
+                    !!localStorage.getItem('auth');
+    return {
+        hasLocalStorageAuth: hasToken
+    };
+})()
+```
+
+### 网站特定检测
+
+不同网站的登录状态检测方式不同,需要根据实际情况调整:
+
+```python
+# 小红书
+check_login_js = """
+(function() {
+    const userAvatar = document.querySelector('.user-avatar');
+    return { isLoggedIn: !!userAvatar };
+})()
+"""
+
+# 知乎
+check_login_js = """
+(function() {
+    const userLink = document.querySelector('.AppHeader-userInfo');
+    return { isLoggedIn: !!userLink };
+})()
+"""
+
+# 微博
+check_login_js = """
+(function() {
+    const userName = document.querySelector('.gn_name');
+    return { isLoggedIn: !!userName };
+})()
+"""
+```
+
+---
+
+## Cookie管理最佳实践
+
+### 1. Cookie 存储结构
+
+```json
+{
+  "website": "xiaohongshu.com",
+  "saved_at": "2026-01-30T10:00:00",
+  "expires_at": "2026-02-30T10:00:00",
+  "cookies": [
+    {
+      "name": "session_id",
+      "value": "abc123...",
+      "domain": ".xiaohongshu.com",
+      "path": "/",
+      "secure": true,
+      "httpOnly": true
+    }
+  ]
+}
+```
+
+### 2. Cookie 管理类
+
+```python
+import json
+from pathlib import Path
+from datetime import datetime, timedelta
+from typing import List, Dict, Optional
+
+class CookieManager:
+    """Cookie 管理器"""
+
+    def __init__(self, storage_dir: str = "cookies"):
+        self.storage_dir = Path(storage_dir)
+        self.storage_dir.mkdir(parents=True, exist_ok=True)
+
+    def save_cookies(
+        self,
+        website: str,
+        cookies: List[Dict],
+        expires_days: int = 30
+    ):
+        """保存 Cookie"""
+        cookie_file = self.storage_dir / f"{website}.json"
+
+        data = {
+            "website": website,
+            "saved_at": datetime.now().isoformat(),
+            "expires_at": (datetime.now() + timedelta(days=expires_days)).isoformat(),
+            "cookies": cookies
+        }
+
+        with open(cookie_file, "w", encoding="utf-8") as f:
+            json.dump(data, f, ensure_ascii=False, indent=2)
+
+        print(f"✅ Cookie 已保存: {cookie_file}")
+
+    def load_cookies(self, website: str) -> Optional[List[Dict]]:
+        """加载 Cookie"""
+        cookie_file = self.storage_dir / f"{website}.json"
+
+        if not cookie_file.exists():
+            print(f"❌ Cookie 文件不存在: {cookie_file}")
+            return None
+
+        with open(cookie_file, "r", encoding="utf-8") as f:
+            data = json.load(f)
+
+        # 检查是否过期
+        expires_at = datetime.fromisoformat(data["expires_at"])
+        if datetime.now() > expires_at:
+            print(f"⚠️ Cookie 已过期: {cookie_file}")
+            return None
+
+        print(f"✅ Cookie 已加载: {len(data['cookies'])} 个")
+        return data["cookies"]
+
+    def is_valid(self, website: str) -> bool:
+        """检查 Cookie 是否有效"""
+        cookies = self.load_cookies(website)
+        return cookies is not None
+
+# 使用示例
+cookie_manager = CookieManager()
+
+# 保存 Cookie
+cookie_manager.save_cookies(
+    website="xiaohongshu",
+    cookies=[...],
+    expires_days=30
+)
+
+# 加载 Cookie
+cookies = cookie_manager.load_cookies("xiaohongshu")
+if cookies:
+    # 使用 Cookie
+    profile = BrowserProfile(cookies=cookies)
+```
+
+### 3. 自动刷新 Cookie
+
+```python
+async def auto_refresh_cookies(website: str, cookie_manager: CookieManager):
+    """自动刷新 Cookie"""
+
+    # 检查 Cookie 是否有效
+    if cookie_manager.is_valid(website):
+        print("✅ Cookie 有效,直接使用")
+        cookies = cookie_manager.load_cookies(website)
+        return cookies
+
+    # Cookie 无效,重新登录
+    print("⚠️ Cookie 无效,需要重新登录")
+
+    # 启动浏览器并等待用户登录
+    browser, tools = await init_browser_session(
+        headless=False,
+        use_cloud=True,
+    )
+
+    await navigate_to_url(f"https://www.{website}.com")
+    await wait(3)
+
+    await wait_for_user_action(
+        message="请完成登录,然后按 Enter",
+        timeout=300
+    )
+
+    # 获取新的 Cookie
+    get_cookies_js = "(function() { return document.cookie; })()"
+    cookies_result = await evaluate(get_cookies_js)
+
+    # 解析并保存
+    cookies = parse_cookies(cookies_result.output, website)
+    cookie_manager.save_cookies(website, cookies)
+
+    await cleanup_browser_session()
+
+    return cookies
+```
+
+---
+
+## 常见问题
+
+### Q1: Live URL 在哪里找?
+
+**A**: 云浏览器启动时会在日志中输出,格式如下:
+
+```
+INFO [cloud] 🔗 Live URL: https://live.browser-use.com?wss=https%3A%2F%2F...
+```
+
+复制这个 URL 并在浏览器中打开即可。
+
+### Q2: 为什么 Live URL 打不开?
+
+**A**: 可能的原因:
+1. 网络问题 - 检查网络连接
+2. 浏览器已关闭 - 确保云浏览器还在运行
+3. URL 复制不完整 - 确保复制了完整的 URL
+
+### Q3: Cookie 多久会过期?
+
+**A**: 不同网站的 Cookie 过期时间不同:
+- 会话 Cookie: 浏览器关闭后失效
+- 持久 Cookie: 通常 7-30 天
+- 建议定期(每周)重新获取 Cookie
+
+### Q4: 如何处理验证码?
+
+**A**: 验证码必须手动处理:
+1. 使用 `wait_for_user_action` 暂停
+2. 用户在 Live URL 中完成验证码
+3. 按 Enter 继续
+
+### Q5: 可以同时登录多个账号吗?
+
+**A**: 可以,但需要:
+1. 为每个账号保存独立的 Cookie 文件
+2. 使用不同的 BrowserProfile
+3. 或者使用多个浏览器会话
+
+### Q6: 登录后如何保持会话?
+
+**A**:
+1. 不要调用 `cleanup_browser_session()`
+2. 浏览器会话会一直保持
+3. 可以继续执行多个任务
+
+---
+
+## 安全建议
+
+### 1. 保护敏感信息
+
+```python
+# ❌ 不要硬编码密码
+password = "my_password"
+
+# ✅ 使用环境变量
+import os
+password = os.getenv("MY_PASSWORD")
+
+# ✅ 使用配置文件(不提交到 Git)
+import json
+with open("config.json") as f:
+    config = json.load(f)
+    password = config["password"]
+```
+
+### 2. Cookie 文件安全
+
+```bash
+# .gitignore 中添加
+cookies/
+*.cookies.json
+config.json
+.env
+```
+
+### 3. 使用代理
+
+```python
+from browser_use.browser.profile import ProxySettings
+
+proxy = ProxySettings(
+    server="http://proxy.example.com:8080",
+    username="user",
+    password="pass"
+)
+
+profile = BrowserProfile(proxy=proxy)
+```
+
+### 4. 限制登录频率
+
+```python
+import time
+
+# 避免频繁登录
+last_login_time = 0
+MIN_LOGIN_INTERVAL = 3600  # 1小时
+
+def should_login():
+    global last_login_time
+    current_time = time.time()
+    if current_time - last_login_time < MIN_LOGIN_INTERVAL:
+        return False
+    last_login_time = current_time
+    return True
+```
+
+---
+
+## 总结
+
+### 登录处理决策树
+
+```
+需要登录?
+├─ 是
+│  ├─ 有保存的 Cookie?
+│  │  ├─ 是 → 使用 Cookie 复用(方式2)
+│  │  └─ 否 → 继续
+│  ├─ 是扫码/验证码登录?
+│  │  ├─ 是 → 使用手动登录(方式1)
+│  │  └─ 否 → 继续
+│  └─ 是简单账号密码?
+│     ├─ 是 → 可尝试自动化登录(方式3)
+│     └─ 否 → 使用手动登录(方式1)
+└─ 否 → 直接访问
+```
+
+### 推荐方案
+
+1. **首次使用**: 手动登录 + 保存 Cookie
+2. **日常使用**: Cookie 复用
+3. **Cookie 过期**: 手动登录 + 更新 Cookie
+
+### 最佳实践
+
+- ✅ 优先使用手动登录(最可靠)
+- ✅ 保存 Cookie 以便复用
+- ✅ 定期更新 Cookie
+- ✅ 使用非无头模式方便调试
+- ✅ 设置合理的超时时间
+- ❌ 避免硬编码密码
+- ❌ 不要频繁自动化登录
+- ❌ 不要提交 Cookie 文件到 Git
+
+---
+
+**更新时间**: 2026-01-30
+**相关文档**: [CLOUD_BROWSER_GUIDE.md](./CLOUD_BROWSER_GUIDE.md)

+ 370 - 0
examples/LOGIN_UPDATE_SUMMARY.md

@@ -0,0 +1,370 @@
+# 登录处理功能更新总结
+
+## ✅ 已完成的工作
+
+### 1. 修改示例6 - 添加完整的登录处理流程
+
+**文件**: `examples/cloud_browser_example.py`
+
+**主要改进**:
+- ✅ 添加了 `wait_for_user_action` 工具导入
+- ✅ 重构了 `example_6_xhs_search_save()` 函数
+- ✅ 添加了 5 个清晰的步骤标注
+- ✅ 实现了登录状态检测
+- ✅ 集成了手动登录等待机制
+- ✅ 添加了详细的用户提示信息
+- ✅ 增加了结果预览功能
+
+**新增功能**:
+
+```python
+# 步骤 1: 访问小红书首页
+await navigate_to_url("https://www.xiaohongshu.com")
+
+# 步骤 2: 检查登录状态
+check_login_js = """..."""  # JavaScript 检测代码
+login_status = await evaluate(check_login_js)
+
+# 步骤 3: 等待用户手动登录
+await wait_for_user_action(
+    message="请在云浏览器中完成小红书登录,完成后按 Enter 继续",
+    timeout=300  # 5 分钟超时
+)
+
+# 步骤 4: 执行搜索和数据提取
+# 步骤 5: 保存结果并显示预览
+```
+
+**运行方式**:
+```bash
+python examples/cloud_browser_example.py --example 6
+```
+
+---
+
+### 2. 创建详细的登录处理文档
+
+**文件**: `examples/LOGIN_HANDLING_GUIDE.md`
+
+**文档内容** (约 800 行):
+
+#### 📚 主要章节
+
+1. **概述** - 为什么需要登录处理
+2. **登录场景分类** - 5 种常见登录场景
+3. **三种登录处理方式对比**
+   - 方式1: 手动登录(推荐)
+   - 方式2: Cookie复用
+   - 方式3: 自动化登录
+
+4. **方式1: 手动登录(推荐)**
+   - 核心思路
+   - 完整代码示例
+   - 关键点说明
+
+5. **方式2: Cookie复用**
+   - 步骤1: 首次登录并保存 Cookie
+   - 步骤2: 使用保存的 Cookie
+   - Cookie 复用的注意事项
+
+6. **方式3: 自动化登录**
+   - 适用场景
+   - 示例代码
+   - 风险警告
+
+7. **完整实战示例** - 小红书搜索(带登录处理)
+
+8. **登录状态检测**
+   - 通用检测方法
+   - 网站特定检测
+
+9. **Cookie管理最佳实践**
+   - Cookie 存储结构
+   - Cookie 管理类
+   - 自动刷新 Cookie
+
+10. **常见问题** - 6 个常见问题及解答
+
+11. **安全建议**
+    - 保护敏感信息
+    - Cookie 文件安全
+    - 使用代理
+    - 限制登录频率
+
+12. **总结**
+    - 登录处理决策树
+    - 推荐方案
+    - 最佳实践
+
+---
+
+## 🎯 核心解决方案
+
+### 问题1: 小红书需要登录时,在哪一步去登录?
+
+**答案**: 在访问小红书首页后,执行搜索之前
+
+**具体步骤**:
+
+```python
+# 1. 启动云浏览器(非无头模式)
+await init_browser_session(
+    headless=False,  # 关键:设置为 False
+    use_cloud=True,
+)
+
+# 2. 访问小红书首页
+await navigate_to_url("https://www.xiaohongshu.com")
+
+# 3. 检查登录状态(可选)
+login_status = await evaluate(check_login_js)
+
+# 4. 等待用户手动登录 ⭐ 关键步骤
+await wait_for_user_action(
+    message="请在云浏览器中完成小红书登录,完成后按 Enter 继续",
+    timeout=300
+)
+
+# 5. 继续执行搜索和数据提取
+await navigate_to_url(search_url)
+```
+
+**用户操作流程**:
+1. 程序启动后,查找日志中的 `🔗 Live URL`
+2. 复制 Live URL 并在浏览器中打开
+3. 在 Live URL 页面中完成登录(扫码或账号密码)
+4. 登录成功后,回到终端按 Enter 继续
+
+---
+
+### 问题2: 如果后续操作需要登录后才能获得数据,应该在哪一步介入?
+
+**答案**: 有三种介入方式,根据场景选择
+
+#### 方式1: 手动登录(推荐,适用于所有场景)
+
+**介入时机**: 在访问需要登录的页面之前
+
+```python
+# 通用模板
+async def task_with_login():
+    # 1. 启动浏览器
+    await init_browser_session(headless=False, use_cloud=True)
+
+    # 2. 访问网站首页
+    await navigate_to_url("https://target-website.com")
+
+    # 3. 【介入点】等待用户登录
+    await wait_for_user_action(
+        message="请完成登录,然后按 Enter",
+        timeout=300
+    )
+
+    # 4. 继续执行需要登录的操作
+    await navigate_to_url("https://target-website.com/protected-page")
+    # 提取数据...
+```
+
+#### 方式2: Cookie复用(推荐,适用于频繁使用)
+
+**介入时机**: 在初始化浏览器时
+
+```python
+# 首次使用:保存 Cookie
+async def first_time_save_cookies():
+    await init_browser_session(headless=False, use_cloud=True)
+    await navigate_to_url("https://target-website.com")
+
+    # 【介入点】等待用户登录
+    await wait_for_user_action("请完成登录", timeout=300)
+
+    # 保存 Cookie
+    cookies = await get_cookies()
+    save_to_file(cookies, "cookies.json")
+
+# 后续使用:加载 Cookie
+async def subsequent_use():
+    cookies = load_from_file("cookies.json")
+    profile = BrowserProfile(cookies=cookies)
+
+    # 【介入点】使用 profile 初始化
+    await init_browser_session(
+        headless=True,
+        use_cloud=True,
+        browser_profile=profile  # 预设 Cookie
+    )
+
+    # 直接访问需要登录的页面(已登录)
+    await navigate_to_url("https://target-website.com/protected-page")
+```
+
+#### 方式3: 自动化登录(不推荐,仅适用于简单场景)
+
+**介入时机**: 在登录页面加载后
+
+```python
+async def automated_login():
+    await init_browser_session(headless=True, use_cloud=True)
+    await navigate_to_url("https://target-website.com/login")
+
+    # 【介入点】自动填写表单
+    await input_text(index=0, text="username")
+    await input_text(index=1, text="password")
+    await click_element(index=2)
+
+    # 等待登录完成
+    await wait(3)
+
+    # 继续执行任务
+    await navigate_to_url("https://target-website.com/protected-page")
+```
+
+---
+
+## 📊 三种方式对比
+
+| 方式 | 介入时机 | 适用场景 | 优点 | 缺点 |
+|------|---------|---------|------|------|
+| **手动登录** | 访问网站后 | 所有场景 | 最可靠,成功率高 | 需要人工操作 |
+| **Cookie复用** | 初始化浏览器时 | 频繁使用 | 快速,无需重复登录 | Cookie会过期 |
+| **自动化登录** | 登录页面加载后 | 简单账号密码 | 完全自动化 | 容易被检测 |
+
+---
+
+## 🎓 实战建议
+
+### 首次使用(推荐流程)
+
+```python
+# 第一次:手动登录 + 保存 Cookie
+async def first_time():
+    # 1. 手动登录
+    await init_browser_session(headless=False, use_cloud=True)
+    await navigate_to_url("https://www.xiaohongshu.com")
+    await wait_for_user_action("请完成登录", timeout=300)
+
+    # 2. 保存 Cookie
+    cookies = await get_cookies()
+    save_cookies("xiaohongshu", cookies)
+
+    # 3. 执行任务
+    await do_task()
+```
+
+### 日常使用(推荐流程)
+
+```python
+# 后续:直接使用 Cookie
+async def daily_use():
+    # 1. 加载 Cookie
+    cookies = load_cookies("xiaohongshu")
+
+    # 2. 检查 Cookie 是否有效
+    if not cookies or is_expired(cookies):
+        # Cookie 无效,重新登录
+        await first_time()
+        return
+
+    # 3. 使用 Cookie 初始化
+    profile = BrowserProfile(cookies=cookies)
+    await init_browser_session(
+        headless=True,
+        use_cloud=True,
+        browser_profile=profile
+    )
+
+    # 4. 直接执行任务(已登录)
+    await do_task()
+```
+
+---
+
+## 📁 相关文件
+
+1. **示例代码**: `examples/cloud_browser_example.py`
+   - 示例6: 小红书搜索(带登录)
+   - 运行: `python examples/cloud_browser_example.py --example 6`
+
+2. **详细文档**: `examples/LOGIN_HANDLING_GUIDE.md`
+   - 完整的登录处理指南
+   - 包含所有代码示例和最佳实践
+
+3. **云浏览器指南**: `examples/CLOUD_BROWSER_GUIDE.md`
+   - 云浏览器基础使用
+   - 环境配置和快速开始
+
+4. **快速入门**: `examples/README_CLOUD_BROWSER.md`
+   - 快速开始步骤
+   - 示例列表
+
+---
+
+## 🚀 快速开始
+
+### 运行小红书登录示例
+
+```bash
+# 1. 确保已安装依赖
+pip install python-socks
+
+# 2. 配置环境变量(.env 文件)
+BROWSER_USE_API_KEY=your_api_key_here
+
+# 3. 运行示例6
+python examples/cloud_browser_example.py --example 6
+
+# 4. 按照提示操作
+#    - 查找日志中的 Live URL
+#    - 在浏览器中打开 Live URL
+#    - 完成登录
+#    - 回到终端按 Enter
+```
+
+### 查看结果
+
+```bash
+# 结果保存在
+cat output/xhs.json
+```
+
+---
+
+## 💡 关键要点
+
+1. **Live URL 是关键**
+   - 云浏览器启动时会输出
+   - 在浏览器中打开可以实时查看和操作
+   - 格式: `https://live.browser-use.com?wss=...`
+
+2. **headless=False 很重要**
+   - 登录时必须设置为 False
+   - 否则无法在 Live URL 中看到页面
+
+3. **wait_for_user_action 是核心**
+   - 暂停自动化流程
+   - 等待用户手动操作
+   - 设置合理的超时时间(3-5分钟)
+
+4. **Cookie 复用提高效率**
+   - 首次手动登录并保存 Cookie
+   - 后续使用 Cookie 跳过登录
+   - 定期更新 Cookie(建议每周)
+
+5. **安全第一**
+   - 不要硬编码密码
+   - Cookie 文件不要提交到 Git
+   - 使用环境变量管理敏感信息
+
+---
+
+## 📞 技术支持
+
+- **详细文档**: [LOGIN_HANDLING_GUIDE.md](./LOGIN_HANDLING_GUIDE.md)
+- **云浏览器指南**: [CLOUD_BROWSER_GUIDE.md](./CLOUD_BROWSER_GUIDE.md)
+- **示例代码**: `examples/cloud_browser_example.py`
+- **基础类源码**: `agent/tools/builtin/baseClass.py`
+
+---
+
+**更新时间**: 2026-01-30
+**版本**: v1.0.0

+ 111 - 0
examples/README_CLOUD_BROWSER.md

@@ -0,0 +1,111 @@
+# Browser-Use 云浏览器模式示例
+
+本目录包含 Browser-Use 云浏览器模式的完整示例和文档。
+
+## 📁 文件说明
+
+- **cloud_browser_example.py** - 云浏览器模式示例代码(包含 5 个示例)
+- **CLOUD_BROWSER_GUIDE.md** - 详细使用文档
+
+## 🚀 快速开始
+
+### 1. 安装依赖
+
+```bash
+pip install python-socks
+```
+
+### 2. 配置环境变量
+
+在项目根目录的 `.env` 文件中添加:
+
+```bash
+BROWSER_USE_API_KEY=your_api_key_here
+```
+
+### 3. 运行示例
+
+```bash
+# 运行默认示例(示例 1: 基础导航操作)
+python examples/cloud_browser_example.py
+
+# 运行指定示例
+python examples/cloud_browser_example.py --example 2
+
+# 运行所有示例
+python examples/cloud_browser_example.py --all
+```
+
+## 📚 示例列表
+
+1. **基础导航操作** - 访问网页、获取页面信息、截图
+2. **搜索和内容提取** - 使用搜索引擎、提取页面内容
+3. **使用 BrowserProfile** - 预设 cookies、localStorage、User-Agent
+4. **表单交互** - 填写表单、提交数据
+5. **多标签页操作** - 管理多个标签页
+
+## 📖 详细文档
+
+查看 [CLOUD_BROWSER_GUIDE.md](./CLOUD_BROWSER_GUIDE.md) 获取完整的使用指南,包括:
+
+- 云浏览器 vs 本地浏览器对比
+- 环境配置详解
+- 核心概念和 API 说明
+- 完整示例代码
+- 高级用法
+- 常见问题解答
+- 最佳实践
+
+## 🔑 核心代码
+
+```python
+import asyncio
+from dotenv import load_dotenv
+from agent.tools.builtin.baseClass import (
+    init_browser_session,
+    cleanup_browser_session,
+    navigate_to_url,
+)
+
+load_dotenv()
+
+async def main():
+    # 初始化云浏览器
+    browser, tools = await init_browser_session(
+        headless=True,
+        use_cloud=True,  # 关键:启用云浏览器
+    )
+
+    # 访问网页
+    await navigate_to_url("https://www.baidu.com")
+
+    # 清理
+    await cleanup_browser_session()
+
+asyncio.run(main())
+```
+
+## 🌟 云浏览器优势
+
+- ✅ 无需本地安装 Chrome/Chromium
+- ✅ 可在无头服务器上运行
+- ✅ 提供 Live URL 实时查看
+- ✅ 更好的稳定性和性能
+- ✅ 支持分布式部署
+
+## 🛠️ 技术支持
+
+- **详细文档**: [CLOUD_BROWSER_GUIDE.md](./CLOUD_BROWSER_GUIDE.md)
+- **基础类源码**: `agent/tools/builtin/baseClass.py`
+- **Browser-Use 官方文档**: https://docs.browser-use.com
+
+## 📝 注意事项
+
+1. 确保已安装 `python-socks` 依赖
+2. 确保 `.env` 文件中配置了有效的 `BROWSER_USE_API_KEY`
+3. 云浏览器需要网络连接,可能有轻微延迟
+4. 使用 Live URL 可以实时查看云浏览器操作
+
+---
+
+**更新时间**: 2026-01-30

+ 802 - 0
examples/cloud_browser_example.py

@@ -0,0 +1,802 @@
+"""
+云浏览器模式示例
+Cloud Browser Mode Example
+
+本示例展示如何使用 browser-use 的云浏览器模式进行网页自动化操作。
+云浏览器模式的优势:
+1. 无需本地安装 Chrome/Chromium
+2. 可以在无头服务器上运行
+3. 更好的稳定性和性能
+4. 支持分布式部署
+
+使用前提:
+1. 在 .env 文件中配置 BROWSER_USE_API_KEY
+2. 确保网络连接正常
+"""
+
+import sys
+import os
+import asyncio
+import json
+import re
+from datetime import datetime
+from pathlib import Path
+from urllib.parse import quote
+from dotenv import load_dotenv
+
+# 加载环境变量
+load_dotenv()
+
+# 将项目根目录添加到 Python 路径
+project_root = Path(__file__).parent.parent
+sys.path.insert(0, str(project_root))
+
+# 导入 browser-use 核心类
+from browser_use import BrowserSession, BrowserProfile
+from browser_use.tools.service import Tools
+
+# 导入框架的工具函数
+from agent.tools.builtin.baseClass import (
+    init_browser_session,
+    cleanup_browser_session,
+    navigate_to_url,
+    search_web,
+    get_selector_map,
+    click_element,
+    input_text,
+    screenshot,
+    get_page_html,
+    evaluate,
+    wait,
+    scroll_page,
+    wait_for_user_action,
+)
+
+
+async def example_1_basic_navigation():
+    """
+    示例 1: 基础导航操作
+    演示如何使用云浏览器访问网页
+    """
+    print("\n" + "="*60)
+    print("示例 1: 基础导航操作")
+    print("="*60)
+
+    try:
+        # 初始化云浏览器会话
+        # 关键参数:is_local=False 表示使用云浏览器
+        api_key = os.getenv("BROWSER_USE_API_KEY")
+        if not api_key:
+            print("❌ 错误: 未找到 BROWSER_USE_API_KEY,请在 .env 文件中配置")
+            return
+
+        print(f"✅ 使用云浏览器 API Key: {api_key[:20]}...")
+
+        # 初始化浏览器会话(云模式)
+        # 注意:API key 会自动从环境变量 BROWSER_USE_API_KEY 读取
+        browser, tools = await init_browser_session(
+            headless=True,  # 云浏览器通常使用无头模式
+            use_cloud=True,  # 关键:设置为 True 使用云浏览器
+        )
+
+        print("✅ 云浏览器会话已启动")
+
+        # 导航到百度
+        print("\n📍 导航到百度...")
+        result = await navigate_to_url("https://www.baidu.com")
+        print(f"   结果: {result.title}")
+
+        # 等待页面加载
+        await wait(2)
+
+        # 获取页面标题
+        print("\n📄 获取页面信息...")
+        title_result = await evaluate("document.title")
+        print(f"   页面标题: {title_result.output}")
+
+        # 截图
+        print("\n📸 截图...")
+        screenshot_result = await screenshot()
+        print(f"   截图结果: {screenshot_result.title}")
+
+        print("\n✅ 示例 1 完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        # 清理浏览器会话
+        await cleanup_browser_session()
+        print("🧹 浏览器会话已清理")
+
+
+async def example_2_search_and_extract():
+    """
+    示例 2: 搜索和内容提取
+    演示如何使用云浏览器进行搜索并提取内容
+    """
+    print("\n" + "="*60)
+    print("示例 2: 搜索和内容提取")
+    print("="*60)
+
+    try:
+        # 初始化云浏览器
+        api_key = os.getenv("BROWSER_USE_API_KEY")
+        if not api_key:
+            print("❌ 错误: 未找到 BROWSER_USE_API_KEY")
+            return
+
+        browser, tools = await init_browser_session(
+            headless=True,
+            use_cloud=True,
+        )
+
+        print("✅ 云浏览器会话已启动")
+
+        # 使用搜索引擎搜索
+        print("\n🔍 搜索: Python async programming...")
+        result = await search_web("Python async programming", engine="google")
+        print(f"   搜索结果: {result.title}")
+
+        # 等待搜索结果加载
+        await wait(3)
+
+        # 获取页面 HTML(部分)
+        print("\n📄 获取页面 HTML...")
+        html_result = await get_page_html()
+        print(f"   HTML 长度: {len(html_result.metadata.get('html', ''))} 字符")
+
+        # 获取可交互元素
+        print("\n🎯 获取页面元素...")
+        selector_result = await get_selector_map()
+        print(f"   {selector_result.output[:200]}...")
+
+        print("\n✅ 示例 2 完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+        print("🧹 浏览器会话已清理")
+
+
+async def example_3_with_browser_profile():
+    """
+    示例 3: 使用 BrowserProfile 预设配置
+    演示如何使用 BrowserProfile 预设 cookies、localStorage 等
+    """
+    print("\n" + "="*60)
+    print("示例 3: 使用 BrowserProfile 预设配置")
+    print("="*60)
+
+    try:
+        api_key = os.getenv("BROWSER_USE_API_KEY")
+        if not api_key:
+            print("❌ 错误: 未找到 BROWSER_USE_API_KEY")
+            return
+
+        # 创建 BrowserProfile 并预设一些配置
+        profile = BrowserProfile(
+            # 可以预设 cookies
+            cookies=[
+                {
+                    "name": "test_cookie",
+                    "value": "test_value",
+                    "domain": ".example.com",
+                    "path": "/",
+                }
+            ],
+            # 可以预设 localStorage
+            local_storage={
+                "example.com": {
+                    "key1": "value1",
+                    "key2": "value2",
+                }
+            },
+            # 可以设置用户代理
+            user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
+        )
+
+        print("✅ 创建了 BrowserProfile 配置")
+
+        # 使用 profile 初始化浏览器
+        browser, tools = await init_browser_session(
+            headless=True,
+            use_cloud=True,
+            browser_profile=profile,  # 传入 profile
+        )
+
+        print("✅ 云浏览器会话已启动(带预设配置)")
+
+        # 访问一个网页
+        print("\n📍 导航到示例网站...")
+        result = await navigate_to_url("https://httpbin.org/headers")
+        print(f"   结果: {result.title}")
+
+        await wait(2)
+
+        # 检查 User-Agent 是否生效
+        print("\n🔍 检查 User-Agent...")
+        ua_result = await evaluate("navigator.userAgent")
+        print(f"   User-Agent: {ua_result.output[:100]}...")
+
+        print("\n✅ 示例 3 完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+        print("🧹 浏览器会话已清理")
+
+
+async def example_4_form_interaction():
+    """
+    示例 4: 表单交互
+    演示如何在云浏览器中进行表单填写和提交
+    """
+    print("\n" + "="*60)
+    print("示例 4: 表单交互")
+    print("="*60)
+
+    try:
+        api_key = os.getenv("BROWSER_USE_API_KEY")
+        if not api_key:
+            print("❌ 错误: 未找到 BROWSER_USE_API_KEY")
+            return
+
+        browser, tools = await init_browser_session(
+            headless=True,
+            use_cloud=True,
+        )
+
+        print("✅ 云浏览器会话已启动")
+
+        # 访问一个有表单的测试页面
+        print("\n📍 导航到表单测试页面...")
+        result = await navigate_to_url("https://httpbin.org/forms/post")
+        print(f"   结果: {result.title}")
+
+        await wait(2)
+
+        # 获取页面元素
+        print("\n🎯 获取页面元素...")
+        selector_result = await get_selector_map()
+        print(f"   找到 {selector_result.long_term_memory}")
+
+        # 注意:实际使用时需要根据页面结构找到正确的元素索引
+        # 这里只是演示流程
+
+        print("\n✅ 示例 4 完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+        print("🧹 浏览器会话已清理")
+
+
+async def example_5_multi_tab():
+    """
+    示例 5: 多标签页操作
+    演示如何在云浏览器中管理多个标签页
+    """
+    print("\n" + "="*60)
+    print("示例 5: 多标签页操作")
+    print("="*60)
+
+    try:
+        api_key = os.getenv("BROWSER_USE_API_KEY")
+        if not api_key:
+            print("❌ 错误: 未找到 BROWSER_USE_API_KEY")
+            return
+
+        browser, tools = await init_browser_session(
+            headless=True,
+            use_cloud=True,
+        )
+
+        print("✅ 云浏览器会话已启动")
+
+        # 在第一个标签页打开百度
+        print("\n📍 标签页 1: 打开百度...")
+        result1 = await navigate_to_url("https://www.baidu.com")
+        print(f"   结果: {result1.title}")
+
+        await wait(2)
+
+        # 在新标签页打开谷歌
+        print("\n📍 标签页 2: 打开谷歌(新标签页)...")
+        result2 = await navigate_to_url("https://www.google.com", new_tab=True)
+        print(f"   结果: {result2.title}")
+
+        await wait(2)
+
+        # 获取当前页面信息
+        print("\n📄 当前页面信息...")
+        title_result = await evaluate("document.title")
+        print(f"   当前标题: {title_result.output}")
+
+        print("\n✅ 示例 5 完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+        print("🧹 浏览器会话已清理")
+
+
+def load_cookies(cookie_str, domain, url=None):
+    cookies = []
+    try:
+        for cookie_part in cookie_str.split(';'):
+            if  cookie_part:
+                name, value = cookie_part.split('=', 1)
+                cookie = {"name": str(name).strip(), "value": str(value).strip(), "domain": domain,
+                    "path":"/",
+                    "expires":-1,
+                    "httpOnly": False,
+                    "secure": True,
+                    "sameSite":"None"}
+                if url:
+                    cookie["url"] = url
+                cookies.append(cookie)
+    except:
+       pass
+    return cookies
+
+async def example_6_xhs_search_save():
+    """
+    示例 6: 小红书搜索并保存结果(带登录)
+    演示如何处理需要登录的网站
+    """
+    print("\n" + "="*60)
+    print("示例 6: 小红书搜索并保存结果(带登录)")
+    print("="*60)
+
+    try:
+        api_key = os.getenv("BROWSER_USE_API_KEY")
+        if not api_key:
+            print("❌ 错误: 未找到 BROWSER_USE_API_KEY")
+            return
+
+        # 创建 BrowserProfile
+       
+        cookiesStr = "gid=yjJiiqSqKKf8yjJiiqSJiWMKyJvfq2vIJxYDh4EfAyCW9Sq89uUhxI888y4JW8y8WJS448Kj; a1=19a5821e25frfgqcz1g48ktmjilzla6dvt8saird230000337474; webId=bf5a89012d3e96b8e8317a9158d2237b; abRequestId=bf5a89012d3e96b8e8317a9158d2237b; x-user-id-pgy.xiaohongshu.com=64cb5fa2000000002b00a903; x-user-id-ad.xiaohongshu.com=67078bac000000001d022a25; x-user-id-mcc.xiaohongshu.com=67078bac000000001d022a25; web_session=040069b5bf1ceafef95542ee0a3b4b114d9a59; x-user-id-pro.xiaohongshu.com=67078bac000000001d022a25; x-user-id-creator.xiaohongshu.com=64cb5fa2000000002b00a903; webBuild=5.8.0; unread={%22ub%22:%226972cc62000000001a032ef0%22%2C%22ue%22:%226978c695000000001a030baf%22%2C%22uc%22:25}; acw_tc=0a0d0d6817697823078311273e2749a170e3d6e7c28bc3c6b3df1b05366b21; xsecappid=ugc; websectiga=f47eda31ec99545da40c2f731f0630efd2b0959e1dd10d5fedac3dce0bd1e04d; sec_poison_id=8f37e824-4cf9-4c1a-8a6b-1297a36d51ba; customer-sso-sid=68c517601157138359418885nha1gpvvujwqbhia; customerClientId=609975161834570; access-token-creator.xiaohongshu.com=customer.creator.AT-68c517601157138359418887mosxcziw5qwkllrs; galaxy_creator_session_id=NIUNVxmv6LPmZ31jZ2DoKYgyUutPOItjJ24t; galaxy.creator.beaker.session.id=1769782309631057230248; loadts=1769782310288"
+        
+        cookie_url = "https://www.xiaohongshu.com"
+        cookies = load_cookies(cookiesStr, ".xiaohongshu.com", cookie_url)
+
+        profile = BrowserProfile(
+            user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
+        )
+
+        # 初始化云浏览器(非无头模式,方便用户看到登录界面)
+        browser, tools = await init_browser_session(
+            headless=False,  # 设置为 False,方便用户看到浏览器界面
+            use_cloud=True,
+            browser_profile=profile,
+        )
+
+        print("✅ 云浏览器会话已启动")
+        print("📝 提示: 云浏览器启动时会输出 Live URL,你可以在浏览器中打开查看")
+
+        # 步骤 1: 先访问小红书首页,检查是否需要登录
+        print("\n📍 步骤 1: 访问小红书首页...")
+        await navigate_to_url("https://www.xiaohongshu.com")
+        await wait(3)
+        await browser._cdp_set_cookies(cookies)
+        await wait(1)
+        await navigate_to_url("https://www.xiaohongshu.com")
+        await wait(3)
+
+        # 检查是否需要登录
+        print("\n🔍 检查登录状态...")
+        check_login_js = """
+        (function() {
+            // 检查是否有登录按钮或登录相关元素
+            const loginBtn = document.querySelector('[class*="login"]') ||
+                           document.querySelector('[href*="login"]') ||
+                           Array.from(document.querySelectorAll('button, a')).find(el => (el.textContent || '').includes('登录'));
+
+            // 检查是否有用户信息(已登录)
+            const userInfo = document.querySelector('[class*="user"]') ||
+                           document.querySelector('[class*="avatar"]');
+
+            return {
+                needLogin: !!loginBtn && !userInfo,
+                hasLoginBtn: !!loginBtn,
+                hasUserInfo: !!userInfo
+            };
+        })()
+        """
+
+        login_status = await evaluate(check_login_js)
+        print(f"   登录状态检查: {login_status.output}")
+        status_output = login_status.output
+        if isinstance(status_output, str) and status_output.startswith("Result: "):
+            status_output = status_output[8:]
+        login_info = None
+        if isinstance(status_output, str):
+            try:
+                login_info = json.loads(status_output)
+            except Exception:
+                login_info = None
+        elif isinstance(status_output, dict):
+            login_info = status_output
+
+        if login_info and login_info.get("needLogin"):
+            print("\n👤 步骤 2: 登录处理...")
+            print("   如果小红书需要登录,请在云浏览器中完成以下操作:")
+            print("   1. 打开上面输出的 Live URL(在日志中查找 '🔗 Live URL')")
+            print("   2. 在 Live URL 页面中完成登录(扫码或账号密码)")
+            print("   3. 登录成功后,回到这里按 Enter 继续")
+            await wait_for_user_action(
+                message="请在云浏览器中完成小红书登录,完成后按 Enter 继续",
+                timeout=300
+            )
+            print("\n✅ 用户已确认登录完成,继续执行...")
+        else:
+            print("\n✅ 已检测为登录状态,跳过手动登录")
+
+        # 步骤 3: 执行搜索
+        keyword = "瑜伽美女"
+        search_url = f"https://www.xiaohongshu.com/search_result?keyword={quote(keyword)}&type=51"
+        print(f"\n📍 步骤 3: 导航到搜索页: {keyword} ...")
+        await navigate_to_url(search_url)
+        await wait(6)
+
+        # 滚动页面加载更多内容
+        print("\n📜 滚动页面加载更多内容...")
+        for i in range(3):
+            print(f"   滚动 {i+1}/3...")
+            await scroll_page(down=True, pages=2.0)
+            await wait(2)
+
+        # 步骤 4: 提取数据
+        print("\n📊 步骤 4: 提取搜索结果...")
+        extract_js = """
+        (function(){
+            const maxCount = 20;
+            const seen = new Set();
+            const results = [];
+
+            function pushItem(item){
+                if (!item || !item.link || seen.has(item.link)) return;
+                seen.add(item.link);
+                results.push(item);
+            }
+
+            // 方法 1: 从 DOM 中提取
+            const anchors = document.querySelectorAll('a[href*="/explore/"]');
+            anchors.forEach(a => {
+                if (results.length >= maxCount) return;
+                const link = a.href || '';
+                const img = a.querySelector('img');
+                const title = ((img && img.alt) || a.textContent || '').trim();
+                const cover = (img && img.src) || '';
+                if (link && title) {
+                    pushItem({ title, link, cover });
+                }
+            });
+
+            // 方法 2: 从 JSON 数据中提取
+            const scriptNodes = document.querySelectorAll('script[type="application/json"], script#__NEXT_DATA__, script#__NUXT__');
+            const walk = (node) => {
+                if (!node || results.length >= maxCount) return;
+                if (Array.isArray(node)) {
+                    for (const item of node) {
+                        walk(item);
+                        if (results.length >= maxCount) return;
+                    }
+                    return;
+                }
+                if (typeof node === 'object') {
+                    const title = (node.title || node.desc || node.name || node.noteTitle || '').toString().trim();
+                    const id = node.noteId || node.note_id || node.id || node.noteID;
+                    const cover = (node.cover && (node.cover.url || node.cover.urlDefault)) || node.coverUrl || node.image || '';
+                    let link = '';
+                    if (id) {
+                        link = `https://www.xiaohongshu.com/explore/${id}`;
+                    }
+                    if (title && link) {
+                        pushItem({ title, link, cover });
+                    }
+                    for (const key in node) {
+                        if (typeof node[key] === 'object') walk(node[key]);
+                    }
+                }
+            };
+
+            scriptNodes.forEach(node => {
+                if (results.length >= maxCount) return;
+                const text = node.textContent || '';
+                if (!text) return;
+                try {
+                    const data = JSON.parse(text);
+                    walk(data);
+                } catch (e) {}
+            });
+
+            return {
+                success: true,
+                keyword: '瑜伽美女',
+                count: results.length,
+                results: results,
+                timestamp: new Date().toISOString(),
+            };
+        })()
+        """
+
+        async def run_extract():
+            result = await evaluate(extract_js)
+            output = result.output
+            if isinstance(output, str) and output.startswith("Result: "):
+                output = output[8:]
+
+            try:
+                data = json.loads(output)
+            except Exception:
+                data = {
+                    "success": False,
+                    "keyword": keyword,
+                    "error": "JSON 解析失败",
+                    "raw_output": str(output)[:2000],
+                    "timestamp": datetime.now().isoformat(),
+                }
+
+            if isinstance(data, dict) and data.get("count", 0) == 0:
+                print("   JS 提取结果为空,尝试从 HTML 中提取...")
+                html_result = await get_page_html()
+                html = html_result.metadata.get("html", "")
+                if html:
+                    def decode_text(value: str) -> str:
+                        try:
+                            return bytes(value, "utf-8").decode("unicode_escape")
+                        except Exception:
+                            return value
+
+                    results = []
+                    seen = set()
+                    pattern = re.compile(r'"noteId":"(.*?)".*?"title":"(.*?)"', re.S)
+                    for match in pattern.finditer(html):
+                        note_id = match.group(1)
+                        title = decode_text(match.group(2)).strip()
+                        link = f"https://www.xiaohongshu.com/explore/{note_id}"
+                        if note_id and link not in seen and title:
+                            seen.add(link)
+                            results.append({"title": title, "link": link})
+                        if len(results) >= 20:
+                            break
+
+                    if results:
+                        data = {
+                            "success": True,
+                            "keyword": keyword,
+                            "count": len(results),
+                            "results": results,
+                            "timestamp": datetime.now().isoformat(),
+                            "source": "html_fallback",
+                        }
+                    else:
+                        blocked_markers = ["登录", "验证", "验证码", "请先登录", "异常访问"]
+                        if any(marker in html for marker in blocked_markers):
+                            data = {
+                                "success": False,
+                                "keyword": keyword,
+                                "count": 0,
+                                "results": [],
+                                "error": "可能被登录或验证码拦截",
+                                "timestamp": datetime.now().isoformat(),
+                            }
+            return data
+
+        data = await run_extract()
+        if isinstance(data, dict) and data.get("count", 0) == 0 and data.get("error") == "可能被登录或验证码拦截":
+            print("\n👤 检测到拦截,请在云浏览器中完成登录或验证码验证")
+            await wait_for_user_action(
+                message="完成后按 Enter 继续,将重新提取搜索结果",
+                timeout=300
+            )
+            data = await run_extract()
+
+        # 步骤 5: 保存结果
+        print(f"\n💾 步骤 5: 保存结果...")
+        print(f"   提取到 {data.get('count', 0)} 条数据")
+
+        output_dir = Path(__file__).parent.parent / "output"
+        output_dir.mkdir(parents=True, exist_ok=True)
+        output_path = output_dir / "xhs.json"
+        with open(output_path, "w", encoding="utf-8") as f:
+            json.dump(data, f, ensure_ascii=False, indent=2)
+
+        print(f"✅ 数据已保存到: {output_path}")
+
+        # 显示部分结果
+        if data.get("results"):
+            print(f"\n📋 前 3 条结果预览:")
+            for i, item in enumerate(data["results"][:3], 1):
+                print(f"   {i}. {item.get('title', 'N/A')[:50]}")
+                print(f"      {item.get('link', 'N/A')}")
+
+        print("\n✅ 示例 6 完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+        import traceback
+        traceback.print_exc()
+    finally:
+        await cleanup_browser_session()
+        print("🧹 浏览器会话已清理")
+
+
+async def example_7_baidu_search_save():
+    print("\n" + "="*60)
+    print("示例 7: 百度搜索并保存结果")
+    print("="*60)
+
+    try:
+        api_key = os.getenv("BROWSER_USE_API_KEY")
+        if not api_key:
+            print("❌ 错误: 未找到 BROWSER_USE_API_KEY")
+            return
+
+        await init_browser_session(
+            headless=True,
+            use_cloud=True,
+        )
+
+        print("✅ 云浏览器会话已启动")
+
+        keyword = "瑜伽美女"
+        search_url = f"https://www.baidu.com/s?wd={quote(keyword)}"
+        print(f"\n📍 导航到百度搜索页: {keyword} ...")
+        await navigate_to_url(search_url)
+        await wait(3)
+        await scroll_page(down=True, pages=1.5)
+        await wait(2)
+
+        extract_js = """
+        (function(){
+            const results = [];
+            const items = document.querySelectorAll('#content_left > div[class*="result"]');
+            items.forEach((item, index) => {
+                if (index >= 10) return;
+                const titleEl = item.querySelector('h3 a, .t a');
+                const title = titleEl ? titleEl.textContent.trim() : '';
+                const link = titleEl ? titleEl.href : '';
+                const summaryEl = item.querySelector('.c-abstract, .content-right_8Zs40');
+                const summary = summaryEl ? summaryEl.textContent.trim() : '';
+                const sourceEl = item.querySelector('.c-color-gray, .source_1Vdff');
+                const source = sourceEl ? sourceEl.textContent.trim() : '';
+                if (title || link) {
+                    results.push({
+                        index: index + 1,
+                        title,
+                        link,
+                        summary: summary.substring(0, 200),
+                        source,
+                    });
+                }
+            });
+            return {
+                success: true,
+                keyword: '瑜伽美女',
+                count: results.length,
+                results,
+                timestamp: new Date().toISOString(),
+            };
+        })()
+        """
+
+        result = await evaluate(extract_js)
+        output = result.output
+        if isinstance(output, str) and output.startswith("Result: "):
+            output = output[8:]
+
+        try:
+            data = json.loads(output)
+        except Exception:
+            data = {
+                "success": False,
+                "keyword": keyword,
+                "error": "JSON 解析失败",
+                "raw_output": str(output)[:2000],
+                "timestamp": datetime.now().isoformat(),
+            }
+
+        output_dir = Path(__file__).parent.parent / "output"
+        output_dir.mkdir(parents=True, exist_ok=True)
+        output_path = output_dir / "baidu.json"
+        with open(output_path, "w", encoding="utf-8") as f:
+            json.dump(data, f, ensure_ascii=False, indent=2)
+
+        print(f"✅ 数据已保存到: {output_path}")
+        if data.get("results"):
+            print("\n📋 前 3 条结果预览:")
+            for i, item in enumerate(data["results"][:3], 1):
+                print(f"   {i}. {item.get('title', 'N/A')[:50]}")
+                print(f"      {item.get('link', 'N/A')}")
+
+        print("\n✅ 示例 7 完成")
+
+    except Exception as e:
+        print(f"❌ 错误: {str(e)}")
+    finally:
+        await cleanup_browser_session()
+        print("🧹 浏览器会话已清理")
+
+
+async def main():
+    """
+    主函数:运行所有示例
+    """
+    import argparse
+
+    print("\n" + "="*60)
+    print("🌐 Browser-Use 云浏览器模式示例")
+    print("="*60)
+
+    # 检查 API Key
+    api_key = os.getenv("BROWSER_USE_API_KEY")
+    if not api_key:
+        print("\n❌ 错误: 未找到 BROWSER_USE_API_KEY")
+        print("请在 .env 文件中配置 BROWSER_USE_API_KEY")
+        return
+
+    print(f"\n✅ 已加载 API Key: {api_key[:20]}...")
+
+    # 运行示例(可以选择运行哪些示例)
+    examples = [
+        ("基础导航操作", example_1_basic_navigation),
+        ("搜索和内容提取", example_2_search_and_extract),
+        ("使用 BrowserProfile", example_3_with_browser_profile),
+        ("表单交互", example_4_form_interaction),
+        ("多标签页操作", example_5_multi_tab),
+        ("小红书搜索并保存结果", example_6_xhs_search_save),
+        ("百度搜索并保存结果", example_7_baidu_search_save),
+    ]
+
+    # 解析命令行参数
+    parser = argparse.ArgumentParser(description="Browser-Use 云浏览器模式示例")
+    parser.add_argument(
+        "--example",
+        type=int,
+        choices=range(1, len(examples) + 1),
+        help="选择要运行的示例 (1-7),不指定则运行第一个示例"
+    )
+    parser.add_argument(
+        "--all",
+        action="store_true",
+        help="运行所有示例"
+    )
+    args = parser.parse_args()
+
+    print("\n可用示例:")
+    for i, (name, _) in enumerate(examples, 1):
+        print(f"  {i}. {name}")
+
+    if args.all:
+        # 运行所有示例
+        print("\n运行所有示例...")
+        for name, func in examples:
+            await func()
+            print("\n" + "-"*60)
+    elif args.example:
+        # 运行指定示例
+        name, func = examples[args.example - 1]
+        print(f"\n运行示例 {args.example}: {name}")
+        await func()
+    else:
+        # 默认运行第一个示例
+        name, func = examples[0]
+        print(f"\n运行默认示例: {name}")
+        print("(使用 --example N 运行其他示例,或 --all 运行所有示例)")
+        await func()
+
+    print("\n" + "="*60)
+    print("✅ 示例运行完成")
+    print("="*60)
+
+
+if __name__ == "__main__":
+    # 运行主函数
+    asyncio.run(main())

+ 2 - 0
requirements.txt

@@ -11,3 +11,5 @@ browser-use>=0.11.0
 fastapi>=0.115.0
 uvicorn[standard]>=0.32.0
 websockets>=13.0
+
+