如何让 AI“看懂”网页?拆解 Browser-Use 的三大核心技术模块(前)
传统的 Browser-Use 多依赖于固定选择器和流程编排,难以应对界面变化与复杂逻辑。随着大模型驱动的智能体技术兴起,Browser-Use 正迈向智能化新阶段:LLM 作为“大脑”负责任务规划与语义理解,结合视觉识别、DOM 分析、动作预测等模块,实现对浏览器环境的感知、决策与执行闭环,从而完成注册、比价、填报、监控等多步骤复杂任务的自主自动化。

一 引言
Browser Use 是一种基于 AI 模型的浏览器自动化技术,其核心目标是通过大模型进行推理和决策,解析用户指令,然后模拟人类操作行为,通过浏览器执行具体的操作(如点击、输入、页面跳转),从而实现对浏览器的自动控制。常用场景例如自动化浏览网页、提取信息、模拟用户操作、自动化测试等。
Browser Use 是基于 LangChain 生态构建的,需要遵循 LangChain 的接口规范,其核心价值在于将 LLM 的语义理解能力与浏览器自动化深度结合。
1、Vision+HTML Extraction
融合视觉理解和HTML结构(DOM树)解析,实现对网页内容的精准定位与交互。
2、Multi-tab Management
自动管理多个浏览器标签页,支持复杂流程(如跨页面数据抓取)和并行任务处理。
3、Element Tracking
记录用户操作的元素 XPath 路径,并复现 LLM 的精确动作,确保自动化的一致性。
4、Custom Actions
可扩展自定义操作(如保存文件、数据库操作、通知)。
5、Self-correcting
自纠错机制,自动检测操作失败(如元素未找到、超时),并尝试恢复流程。
6、Any LLM Support
支持所有 LangChain 兼容的 LLM,实现模型无关的指令解析。
二 历史发展
在 BrowserUse 等 AI 驱动的浏览器自动化工具出现之前,传统 RPA(Robotic Process Automation)、爬虫框架和自动化测试工具已长期服务于数据抓取、页面操作模拟等场景,下面从技术发展历史角度,分阶段解析这些需求的实现方式及演变逻辑。
2.1 早期阶段:脚本化和人工编码
-
技术手段
-
对于实时单次数据获取,通常依赖开发者手动编写 Python 脚本(如 requests + BeautifulSoup),需要精确解析 HTML 结构;
-
对于离线批量数据,可以使用 Scrapy 开源框架,通过定义 Spider 规则批量抓取网页,存储为结构化数据,需要利用 Xpath/CSS 选择器来手动配置字段提取规则;
-
对于自动化测试场景,使用 Selenium,通过代码模拟点击、输入等操作,验证网页能力,虽然支持录制用户操作生成脚本,但仅支持简单流程;
-
局限性
-
针对静态页面,无法处理 JS 动态渲染内容,每次页面结构调整需要人工修改爬取逻辑的代码,维护成本高;
-
对动态内容(如单页 SPA )支持有限,需要额外集成 Selenium 等无头浏览器,反爬机制需人工绕过,自动化程度低;
-
缺乏智能决策能力,无法自动化处理分支逻辑。
2.2 RPA阶段:规则驱动的自动化
-
技术手段
-
基于 UI 元素识别(如按钮、输入框的坐标或属性)和预设流程,通过模拟鼠标键盘操作实现自动化,代表工具有 UiPath、Automation Anywhere、Blue Prism;
-
局限性
-
依赖固定 UI 元素定位,网页布局变动易导致流程中断;
-
缺乏语义理解,无法处理需要逻辑推理的任务(比如根据页面内容选择下一步操作);
-
维护成本高,企业通常需要投入大量资源更新流程脚本以适应系统变更迭代。
2.3 动态网页和反爬对抗阶段:工具链逐渐复杂化
-
技术手段
-
无头浏览器普及,Selenium + Chrome Headless 成为动态网页抓取标配,但资源消耗大;轻量级的工具,像 Puppeteer(Node.js)提供更轻量级控制,但仍需硬编码操作步骤;
-
反爬攻防战,网站采用验证码、IP 限流、动态 Token 等机制,迫使爬虫开发者引入打码平台或代理池。
-
局限性
-
资源消耗和性能瓶颈,浏览器实例占用内存高,难以大规模并行抓取,其对 CPU 和内存的消耗显著高于传统 HTTP 请求;
-
浏览器兼容性限制,Puppeteer 仅原生支持Chromium内核浏览器,而Selenium虽然支持多浏览器,但不同浏览器驱动的API差异导致跨平台脚本维护成本增加;
-
反爬对抗的复杂性升级。
当前工具链本质是模拟人类操作浏览器,无法突破「浏览器沙箱」限制。即便结合代理IP和Puppeteer,面对浏览器指纹检测等新型反爬技术时,仍需引入Puppeteer-extra等插件进行特征伪装,导致工具链复杂度指数级上升
2.4 AI 驱动的范式跃迁
-
需求驱动因素
SPA(单页应用)和 WebAssembly 普及,传统爬虫难以解析完整 DOM;业务场景碎片化,任务需求复杂化,人力成本压力等等。
-
技术成熟条件
大语言模型如 GPT-4 等具备自然语言指令解析与任务规划能力,可将抽象需求转化为操作序列;浏览器自动化框架如 Playwright 提供浏览器控制接口;视觉理解模型可解析屏幕内容,补充 Dom 解析获取的页面信息不足。
2.5 内容小结
BrowserUse 的出现是技术矛盾(动态网页复杂性 vs 传统工具僵化性)与技术进步(LLM+浏览器控制)共同作用的结果,也标志了浏览器自动化从 “规则驱动” 向 “认知驱动” 的范式跃迁。总的来说,其实际价值在于,通过 LLM 的泛化能力减少因网页改版导致的脚本失效问题,支持自动化复杂处理(处理弹窗),以及加速开发效率。

三 核心技术解析
Browser-Use 项目中:
service.py 和 views.py 遵循了经典的分层架构设计模式。
View 层 - 数据定义层:Pydantic 数据模型定义、数据验证、数据格式转换、模块间数据传递的标准格式。
Service 层 - 业务逻辑层:实现核心的功能和算法、管理复杂的操作流程、第三方服务集成、维护对象生命周期。
├── agent # AI 代理│ ├── gif.py # 历史记录可视化│ ├── memory # 记忆模块│ │ ├── __init__.py│ │ ├── service.py│ │ └── views.py│ ├── message_manager # 消息管理│ │ ├── service.py│ │ ├── tests.py│ │ ├── utils.py│ │ └── views.py│ ├── playwright_script_generator.py│ ├── playwright_script_helpers.py│ ├── prompts.py # 提示词相关│ ├── service.py│ ├── system_prompt.md│ ├── tests.py│ └── views.py├── browser # 浏览器相关│ ├── __init__.py│ ├── browser.py│ ├── context.py│ ├── extensions.py│ ├── profile.py # 浏览器配置│ ├── session.py # 核心会话管理│ └── views.py├── cli.py├── controller # 工具Action相关│ ├── registry│ │ ├── service.py│ │ └── views.py│ ├── service.py│ └── views.py├── dom # Dom 树解析&可交互元素处理│ ├── __init__.py│ ├── buildDomTree.js│ ├── clickable_element_processor│ │ └── service.py│ ├── history_tree_processor│ │ ├── service.py│ │ └── view.py│ ├── service.py│ ├── tests│ │ └── test_accessibility_playground.py│ └── views.py├── exceptions.py├── logging_config.py├── telemetry # 产品使用情况追踪,数据收集&分析模块│ ├── __init__.py│ ├── service.py│ └── views.py└── utils.py
3.1.0 模块概览
-
agent
-
gif.py:用于将 AI Agent 的执行历史转换成可视化的动态 GIF 动画,展示整个任务执行过程的,每一步的屏幕截图、任务目标和步骤信息、执行进度和状态;
-
message_manager 模块:管理大模型交互过程中所有通信内容,包括系统提示词、用户输入、模型输出、工具输出等;
-
memory 模块:记忆管理模块(基于 Mem0 的向量存储),专门用于优化长期任务执行中的上下文窗口使用,核心是解决 Token 限制问题(长期任务会产生大量对话历史),智能记忆压缩(对上面的 message 总结&压缩,被压缩的信息不涵盖系统提示词和memory相关的信息)。
-
browser:核心基础设施,负责管理和控制浏览器实例,为 AI Agent 提供与真实浏览器交互的能力,本质上是对 Playwright 进行了一层封装;
-
controller:整个框架的动作执行引擎&Action注册管理,负责将 AI Agent 的决策转换为具体的浏览器操作;
-
dom:整个框架的感知引擎,负责理解和处理网页结构,将复杂的 HTML DOM 转换为 Agent 可以理解和操作的结构化数据;
-
telemetry:追踪 Browser-Use 产品本身使用情况,用于收集用户使用情况,性能指标和错误信息;比如像那个模型成功率更高,哪种任务耗时过长,vision 功能使用频率,常见失败原因,最常用的自定义功能等等;
-
事件发送:将遥测服务发送到分析服务;
-
隐私保护:匿名化敏感数据;
-
配置管理:控制遥测开关和参数;
3.1.1 Dom 树解析
Dom 层核心功能
-
Dom 结构解析与抽象
-
智能元素识别与索引
其中 buildDomTree.js 是 Dom 层的核心组件,运行在浏览器环境中,负责智能识别和处理页面元素。
-
通过递归方式对DOM树进行深度遍历,确保每一个节点都能被准确访问与处理,为标注提供全面的元素信息
-
// 函数入口 function buildDomTree(node, parentIframe = null, isParentHighlighted = false){ // node: 当前要处理的 DOM 节点 // parentIframe: 父级 iframe(用于跨 iframe 处理) // isParentHighlighted: 父节点是否已被高亮(状态传递) } // 递归终止条件 - 防止无限递归 if (!node || node.id === HIGHLIGHT_CONTAINER_ID || (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE)) { if (debugMode) PERF_METRICS.nodeMetrics.skippedNodes++; return null; // 终止当前分支的递归 } // 根节点特殊处理 if (node === document.body) { const nodeData = { tagName: 'body', attributes: {}, xpath: '/body', children: [], }; // 核心递归点1:处理 body 的所有子节点 for (const child of node.childNodes) { const domElement = buildDomTree(child, parentIframe, false); // 🔄 递归调用 if (domElement) nodeData.children.push(domElement); } const id = `${ID.current++}`; DOM_HASH_MAP[id] = nodeData; return id; } // 核心递归点2:根据节点类型进行不同的递归处理 if (node.tagName) { const tagName = node.tagName.toLowerCase(); // 场景1:iframe 递归处理 if (tagName === "iframe") { try { const iframeDoc = node.contentDocument || node.contentWindow?.document; if (iframeDoc) { for (const child of iframeDoc.childNodes) { const domElement = buildDomTree(child, node, false); // 🔄 跨 iframe 递归 if (domElement) nodeData.children.push(domElement); } } } catch (e) { console.warn("Unable to access iframe:", e); } } // 场景2:富文本编辑器递归处理 elseif ( node.isContentEditable || node.getAttribute("contenteditable") === "true" || node.id === "tinymce" || node.classList.contains("mce-content-body") ) { // 处理富文本内容 - 保持高亮状态传递 for (const child of node.childNodes) { const domElement = buildDomTree(child, parentIframe, nodeWasHighlighted); // 🔄 递归 if (domElement) nodeData.children.push(domElement); } } // 场景3:常规元素递归处理 else { // Shadow DOM 处理 if (node.shadowRoot) { nodeData.shadowRoot = true; for (const child of node.shadowRoot.childNodes) { const domElement = buildDomTree(child, parentIframe, nodeWasHighlighted); // 🔄 Shadow DOM 递归 if (domElement) nodeData.children.push(domElement); } } // 最重要的递归处理:常规子节点 for (const child of node.childNodes) { // 关键:高亮状态的递归传递 const passHighlightStatusToChild = nodeWasHighlighted || isParentHighlighted; const domElement = buildDomTree(child, parentIframe, passHighlightStatusToChild); // 🔄 递归调用 if (domElement) nodeData.children.push(domElement); } } } # service.py - _construct_dom_tree 方法 @time_execution_async('--construct_dom_tree') async def _construct_dom_tree(self, eval_page: dict) -> tuple[DOMElementNode, SelectorMap]: """从 JavaScript 结果构建 Python DOM 树 - 核心递归处理""" js_node_map = eval_page['map'] js_root_id = eval_page['rootId'] selector_map = {} node_map = {} # 🔄 第一轮遍历:创建所有节点 for id, node_data in js_node_map.items(): node, children_ids = self._parse_node(node_data) if node is None: continue node_map[id] = node # 建立可交互元素的索引映射 if isinstance(node, DOMElementNode) and node.highlight_index is not None: selector_map[node.highlight_index] = node # 🔄 第二轮遍历:建立父子关系(递归结构) for id, node_data in js_node_map.items(): node = node_map.get(id) if isinstance(node, DOMElementNode): # 关键:递归建立父子关系 for child_id in node_data.get('children', []): if child_id in node_map: child_node = node_map[child_id] child_node.parent = node # 设置父节点 node.children.append(child_node) # 添加子节点 return node_map[str(js_root_id)], selector_map对元素的可交互性和可见性进行精准判断,确保标注仅应用于符合条件的目标元素,提升标注的准确性
classClickableElementProcessor:
"""可点击元素处理器"""
@staticmethod
def get_clickable_elements_hashes(dom_element: DOMElementNode) -> set[str]:
"""获取所有可点击元素的哈希值集合"""
clickable_elements = ClickableElementProcessor.get_clickable_elements(dom_element)
return {ClickableElementProcessor.hash_dom_element(element) for element in clickable_elements}
@staticmethod
def hash_dom_element(dom_element: DOMElementNode) -> str:
"""为 DOM 元素生成唯一哈希标识"""
# 1. 父级路径哈希
parent_branch_path = ClickableElementProcessor._get_parent_branch_path(dom_element)
branch_path_hash = ClickableElementProcessor._parent_branch_path_hash(parent_branch_path)
# 2. 属性哈希
attributes_hash = ClickableElementProcessor._attributes_hash(dom_element.attributes)
# 3. XPath 哈希
xpath_hash = ClickableElementProcessor._xpath_hash(dom_element.xpath)
# 4. 组合哈希
return ClickableElementProcessor._hash_string(f'{branch_path_hash}-{attributes_hash}-{xpath_hash}')
视觉标注实现(高亮系统)
// 元素高亮 - 为 AI 提供视觉索引
function highlightElement(element, index, parentIframe = null){
// 1. 创建高亮容器
let container = document.getElementById(HIGHLIGHT_CONTAINER_ID);
if (!container) {
container = document.createElement("div");
container.id = HIGHLIGHT_CONTAINER_ID;
container.style.zIndex = "2147483640"; // 最高层级
}
// 2. 为每个元素创建彩色边框和数字标签
const colors = ["#FF0000", "#00FF00", "#0000FF", "#FFA500"];
const baseColor = colors[index % colors.length];
// 3. 多矩形支持 (处理复杂布局)
const rects = element.getClientRects();
for (const rect of rects) {
const overlay = document.createElement("div");
overlay.style.border = `2px solid ${baseColor}`;
overlay.style.backgroundColor = baseColor + "1A"; // 10% 透明度
// 设置位置和尺寸...
}
}
Dom 树格式化输出
[1]<header class='app-header' >
[2]<div class='logo' >
公司 LOGO
[3]<nav class='main-nav' >
[4]<a href='/dashboard' >控制台 />
[5]<a href='/projects' >项目管理 />
[6]<div class='user-menu' >
[7]<button class='user-avatar' >
[8]<img alt='用户头像' />
[9]<div class='dropdown-menu' >
[10]<a href='/profile' >个人资料 />
[11]<a href='/settings' >账户设置 />
[12]<button >退出登录 />
[13]<main class='app-content' >
[14]<aside class='sidebar' >
[15]<ul class='nav-list' >
[16]<li >
[17]<a href='/tasks' >任务列表 />
[18]<li >
[19]<a href='/calendar' >日历视图 />
[20]<section class='content-area' >
[21]<div class='toolbar' >
[22]<button class='btn-primary' >新建任务 />
[23]<input type='search' placeholder='搜索任务' />
[24]<select name='filter' >
[25]<option value='all' >全部任务 />
[26]<option value='pending' >待处理 />
[27]<div class='task-list' >
任务列表内容
[28]<div class='task-item' >
[29]<input type='checkbox' />
完成网站设计
[30]<button class='edit-btn' >编辑 />
[31]<button class='delete-btn' >删除 />
*[32]*<button >新出现的按钮 /> # 用 * 标记新元素
# views.py - clickable_elements_to_string 方法
def clickable_elements_to_string(self, include_attributes: list[str] | None = None) -> str:
"""将 DOM 树递归转换为 LLM 可理解的文本格式"""
formatted_text = []
def process_node(node: DOMBaseNode, depth: int) -> None:
"""📍 递归处理函数 - 深度优先遍历和格式化"""
next_depth = int(depth)
depth_str = depth * '\t' # 缩进表示层级
if isinstance(node, DOMElementNode):
# 处理可交互元素
if node.highlight_index is not None:
next_depth += 1
# 格式化当前节点信息
text = node.get_all_text_till_next_clickable_element()
# ... 属性处理和格式化逻辑
formatted_text.append(formatted_line)
# ⭐ 递归处理所有子节点
for child in node.children:
process_node(child, next_depth) # 递归调用
elif isinstance(node, DOMTextNode):
# 处理文本节点
if (not node.has_parent_with_highlight_index() and
node.parent and node.parent.is_visible):
formatted_text.append(f'{depth_str}{node.text}')
process_node(self, 0) # 从根节点开始递归
return '\n'.join(formatted_text)
下一章继续解读
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)