example_browser_use_cdp.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. """
  2. 百度搜索示例 - 使用 browser-use 的 CDP 方式
  3. Baidu Search Example - Using browser-use CDP Method
  4. 使用方法:
  5. 1. 先手动启动 Chrome 并开启远程调试:
  6. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222 --user-data-dir="/tmp/chrome-debug-profile"
  7. 2. 验证 CDP 是否运行:
  8. 访问 http://localhost:9222/json/version
  9. 3. 运行此脚本:
  10. python example_browser_use_cdp.py
  11. 功能:
  12. 1. 打开百度
  13. 2. 搜索"Python 教程"
  14. 3. 提取搜索结果数据并保存到 baidu.json
  15. 4. 保存完整页面 HTML 到 baidu_page.html
  16. """
  17. import asyncio
  18. import json
  19. import sys
  20. import os
  21. from pathlib import Path
  22. from datetime import datetime
  23. # 添加项目路径
  24. sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
  25. from agent.tools import tool, ToolResult
  26. async def baidu_search_with_cdp():
  27. """
  28. 使用 browser-use 的 CDP 连接方式搜索百度
  29. """
  30. print("\n" + "="*80)
  31. print("🚀 开始执行百度搜索任务 (browser-use CDP 版本)")
  32. print("="*80 + "\n")
  33. # 项目根目录
  34. project_root = Path(__file__).parent
  35. try:
  36. # 导入 browser-use
  37. from browser_use import Agent, Tools
  38. from browser_use.browser import BrowserProfile, BrowserSession
  39. # ============================================================
  40. # 步骤 1: 连接到已启动的 Chrome(通过 CDP)
  41. # ============================================================
  42. print("📌 步骤 1: 连接到 Chrome (CDP)...")
  43. # 使用 CDP 连接到手动启动的 Chrome
  44. browser_session = BrowserSession(
  45. browser_profile=BrowserProfile(
  46. cdp_url='http://localhost:9222',
  47. is_local=True
  48. )
  49. )
  50. # 创建工具实例
  51. tools = Tools()
  52. print("✅ 已连接到 Chrome (CDP)\n")
  53. # ============================================================
  54. # 步骤 2 & 3: 使用 Agent 执行搜索任务
  55. # ============================================================
  56. print("📌 步骤 2-5: 执行搜索任务...")
  57. # 创建 Agent(不需要 LLM,直接使用工具)
  58. task = """
  59. 请完成以下任务:
  60. 1. 打开百度首页 https://www.baidu.com
  61. 2. 导航到搜索结果页面:https://www.baidu.com/s?wd=Python 教程
  62. 3. 等待搜索结果加载(等待3秒)
  63. 4. 滚动页面以加载更多内容
  64. 5. 提取页面 HTML 并保存
  65. """
  66. # 直接使用工具而不是 Agent
  67. print("🔧 直接使用 browser-use 工具...\n")
  68. # 导航到百度首页
  69. print(" → 导航到百度首页...")
  70. result = await tools.navigate(
  71. url="https://www.baidu.com",
  72. browser_session=browser_session
  73. )
  74. print(f" ✅ {result.long_term_memory}")
  75. await asyncio.sleep(2)
  76. # 导航到搜索结果
  77. search_keyword = "Python 教程"
  78. search_url = f"https://www.baidu.com/s?wd={search_keyword}"
  79. print(f"\n → 搜索关键词: {search_keyword}")
  80. result = await tools.navigate(
  81. url=search_url,
  82. browser_session=browser_session
  83. )
  84. print(f" ✅ 已导航到搜索结果页面")
  85. await asyncio.sleep(3)
  86. # 滚动页面
  87. print("\n → 滚动页面加载更多内容...")
  88. await tools.scroll(
  89. down=True,
  90. pages=1.0,
  91. browser_session=browser_session
  92. )
  93. await asyncio.sleep(2)
  94. print(" ✅ 页面滚动完成")
  95. # ============================================================
  96. # 步骤 4: 提取搜索结果数据
  97. # ============================================================
  98. print("\n📌 步骤 4: 提取搜索结果数据...")
  99. # 使用 JavaScript 提取数据
  100. extract_js = """
  101. (function(){
  102. try {
  103. // 提取搜索结果
  104. const results = [];
  105. // 百度的搜索结果选择器
  106. const resultItems = document.querySelectorAll('#content_left > div[class*="result"]');
  107. console.log('找到搜索结果数量:', resultItems.length);
  108. resultItems.forEach((item, index) => {
  109. if (index >= 10) return; // 只提取前10个
  110. try {
  111. // 提取标题和链接
  112. const titleEl = item.querySelector('h3 a, .t a');
  113. const title = titleEl ? titleEl.textContent.trim() : '';
  114. const link = titleEl ? titleEl.href : '';
  115. // 提取摘要
  116. const summaryEl = item.querySelector('.c-abstract, .content-right_8Zs40');
  117. const summary = summaryEl ? summaryEl.textContent.trim() : '';
  118. // 提取来源
  119. const sourceEl = item.querySelector('.c-color-gray, .source_1Vdff');
  120. const source = sourceEl ? sourceEl.textContent.trim() : '';
  121. if (title || link) {
  122. results.push({
  123. index: index + 1,
  124. title: title,
  125. link: link,
  126. summary: summary.substring(0, 200), // 限制摘要长度
  127. source: source
  128. });
  129. }
  130. } catch (e) {
  131. console.error('提取单个结果失败:', e);
  132. }
  133. });
  134. return {
  135. success: true,
  136. count: results.length,
  137. keyword: 'Python 教程',
  138. timestamp: new Date().toISOString(),
  139. results: results
  140. };
  141. } catch (e) {
  142. return {
  143. success: false,
  144. error: e.message,
  145. stack: e.stack
  146. };
  147. }
  148. })()
  149. """
  150. result = await tools.evaluate(
  151. code=extract_js,
  152. browser_session=browser_session
  153. )
  154. # 解析结果
  155. try:
  156. # 从 result.extracted_content 中提取数据
  157. output = result.extracted_content or str(result.metadata)
  158. # 尝试解析 JSON
  159. if isinstance(output, str):
  160. # 如果输出包含 "Result:" 前缀,移除它
  161. if output.startswith("Result: "):
  162. output = output[8:]
  163. data = json.loads(output)
  164. else:
  165. data = output
  166. if data.get('success'):
  167. print(f"✅ 成功提取 {data.get('count', 0)} 条搜索结果")
  168. # 保存到 baidu.json
  169. json_file = project_root / "baidu.json"
  170. with open(json_file, 'w', encoding='utf-8') as f:
  171. json.dump(data, f, ensure_ascii=False, indent=2)
  172. print(f"✅ 数据已保存到: {json_file}\n")
  173. # 打印前3条结果预览
  174. if data.get('results'):
  175. print("📋 前3条结果预览:")
  176. for item in data['results'][:3]:
  177. print(f" {item.get('index')}. {item.get('title', '无标题')}")
  178. print(f" 链接: {item.get('link', '')[:60]}...")
  179. print(f" 来源: {item.get('source', '未知')}")
  180. print()
  181. else:
  182. print(f"⚠️ 数据提取失败: {data.get('error', '未知错误')}")
  183. # 保存错误信息
  184. error_data = {
  185. "success": False,
  186. "error": data.get('error'),
  187. "keyword": "Python 教程",
  188. "timestamp": datetime.now().isoformat()
  189. }
  190. json_file = project_root / "baidu.json"
  191. with open(json_file, 'w', encoding='utf-8') as f:
  192. json.dump(error_data, f, ensure_ascii=False, indent=2)
  193. print(f"⚠️ 错误信息已保存到: {json_file}\n")
  194. except json.JSONDecodeError as e:
  195. print(f"⚠️ JSON 解析失败: {e}")
  196. print(f"原始输出: {str(result)[:200]}...\n")
  197. # 保存原始输出
  198. error_data = {
  199. "success": False,
  200. "error": "JSON解析失败",
  201. "raw_output": str(result)[:1000],
  202. "keyword": "Python 教程",
  203. "timestamp": datetime.now().isoformat()
  204. }
  205. json_file = project_root / "baidu.json"
  206. with open(json_file, 'w', encoding='utf-8') as f:
  207. json.dump(error_data, f, ensure_ascii=False, indent=2)
  208. # ============================================================
  209. # 步骤 5: 保存完整页面 HTML
  210. # ============================================================
  211. print("📌 步骤 5: 保存完整页面 HTML...")
  212. # 获取 CDP 会话
  213. cdp = await browser_session.get_or_create_cdp_session()
  214. # 获取页面内容
  215. html_result = await cdp.cdp_client.send.Runtime.evaluate(
  216. params={'expression': 'document.documentElement.outerHTML'},
  217. session_id=cdp.session_id
  218. )
  219. html_content = html_result.get('result', {}).get('value', '')
  220. # 获取 URL 和标题
  221. url = await browser_session.get_current_page_url()
  222. title_result = await cdp.cdp_client.send.Runtime.evaluate(
  223. params={'expression': 'document.title'},
  224. session_id=cdp.session_id
  225. )
  226. title = title_result.get('result', {}).get('value', '')
  227. # 保存 HTML 文件
  228. html_file = project_root / "baidu_page.html"
  229. with open(html_file, 'w', encoding='utf-8') as f:
  230. # 添加一些元信息
  231. meta_info = f"""<!--
  232. 页面标题: {title}
  233. 页面URL: {url}
  234. 保存时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
  235. 搜索关键词: Python 教程
  236. -->
  237. """
  238. f.write(meta_info)
  239. f.write(html_content)
  240. print(f"✅ HTML 已保存到: {html_file}")
  241. print(f" 页面标题: {title}")
  242. print(f" 页面URL: {url}")
  243. print(f" HTML 大小: {len(html_content):,} 字符\n")
  244. # ============================================================
  245. # 任务完成
  246. # ============================================================
  247. print("="*80)
  248. print("🎉 任务完成!")
  249. print("="*80)
  250. print(f"📁 生成的文件:")
  251. print(f" 1. baidu.json - 搜索结果数据")
  252. print(f" 2. baidu_page.html - 完整页面HTML")
  253. print("="*80 + "\n")
  254. print("💡 提示:Chrome 窗口保持打开状态,您可以继续使用")
  255. print(" 如需关闭,请在 Chrome 中手动关闭\n")
  256. except Exception as e:
  257. print(f"\n❌ 任务执行失败: {str(e)}")
  258. import traceback
  259. traceback.print_exc()
  260. async def main():
  261. """主函数"""
  262. print("\n" + "="*80)
  263. print("⚠️ 使用前请确保:")
  264. print("="*80)
  265. print("1. 已手动启动 Chrome 并开启远程调试端口 9222")
  266. print("2. 启动命令示例:")
  267. print(' "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \\')
  268. print(' --remote-debugging-port=9222 \\')
  269. print(' --user-data-dir="/tmp/chrome-debug-profile"')
  270. print()
  271. print("3. 验证 CDP 是否运行:访问 http://localhost:9222/json/version")
  272. print("="*80)
  273. input("\n按 Enter 键继续(确保已启动 Chrome)...")
  274. await baidu_search_with_cdp()
  275. if __name__ == "__main__":
  276. # 运行任务
  277. asyncio.run(main())