1. 项目概述:AI驱动的网页自动化探索工具
在传统网页自动化领域,我们通常需要手动编写XPath或CSS选择器来定位页面元素。这种方式存在明显的局限性:当页面结构发生变化时,选择器很容易失效,维护成本高,且难以跨网站复用。最近,我基于Tarsier开源工具构建了一个AI驱动的网页自动化探索工具,它通过视觉标注技术,让AI模型能够"看懂"网页并自主进行操作决策。
这个工具的核心创新点在于:它不再依赖硬编码的选择器,而是通过给网页元素添加可视化标签(如[1]、[2]、[3]),并建立标签ID到DOM元素的映射关系。当视觉大模型识别出"需要点击右上角的登录按钮"时,系统会自动将这一视觉指令转换为对具体DOM元素的操作。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术原理与架构设计
2.1 传统自动化工具的局限性
传统工具如Selenium和Playwright的工作方式可以概括为:
python复制# 传统方式:硬编码选择器
driver.find_element(By.CSS_SELECTOR, "#login-btn").click()
driver.find_element(By.XPATH, "//input[@name='username']").send_keys("test")
这种方式存在三个主要问题:
- 脆弱性:页面结构的微小变化就会导致选择器失效
- 维护成本高:每个页面都需要人工分析DOM结构
- 缺乏泛化能力:无法跨网站复用,每个新网站都需要重新编写选择器
2.2 Tarsier的核心工作机制
Tarsier通过以下流程解决了上述问题:
- 元素标注:遍历DOM树,识别所有可交互元素并添加可视化标签
- 映射建立:创建标签ID到XPath的映射表
- 视觉理解:AI模型分析带标签的截图,输出操作指令
- 指令执行:系统根据指令和映射表执行具体操作
mermaid复制graph TD
A[网页加载] --> B[元素标注]
B --> C[截图+映射]
C --> D[视觉模型分析]
D --> E[操作指令]
E --> F[执行操作]
2.3 关键技术实现细节
2.3.1 可交互元素识别
Tarsier通过以下标准识别可交互元素:
javascript复制const INTERACTIVE_ELEMENTS = [
'a', 'button', 'input', 'select', 'textarea',
'[role="button"]', '[role="link"]', '[onclick]'
];
2.3.2 标签注入与样式设计
为每个可交互元素注入可视化标签:
javascript复制const tag = document.createElement('div');
tag.textContent = `[${id}]`;
tag.style.cssText = `
position: absolute;
background: #FF6B6B;
color: white;
font-size: 12px;
padding: 2px 4px;
border-radius: 3px;
z-index: 10000;
`;
element.parentNode.insertBefore(tag, element);
2.3.3 元素分类系统
Tarsier使用前缀区分不同类型的元素:
| 前缀 | 元素类型 | 示例 |
|---|---|---|
| [#ID] | 输入框 | 文本框、密码框 |
| [@ID] | 链接 | 超链接、锚点 |
| [$ID] | 按钮/交互元素 | 按钮、下拉菜单 |
| [%ID] | 图片元素 | 可点击的图片 |
这种分类帮助AI模型更好地理解元素的用途和操作方式。
3. 系统实现与集成
3.1 环境准备与基础使用
安装所需依赖:
bash复制pip install tarsier playwright openai
playwright install chromium
基础使用示例:
python复制import asyncio
from playwright.async_api import async_playwright
from tarsier import Tarsier, GoogleVisionOCRService
async def basic_demo():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto("https://example.com")
# 标注页面
tarsier = Tarsier(GoogleVisionOCRService(api_key="..."))
screenshot, tag_to_xpath = await tarsier.page_to_image(page)
print(f"发现 {len(tag_to_xpath)} 个可交互元素")
await browser.close()
asyncio.run(basic_demo())
3.2 与视觉大模型集成
创建视觉大模型客户端:
python复制from openai import OpenAI
class VisionLLMClient:
SYSTEM_PROMPT = """你是一个网页自动化助手。
页面上的可交互元素已被标记:[#ID] 输入框、[@ID] 链接、[$ID] 按钮
输出操作:CLICK [ID] / TYPE [ID] "文本" / SCROLL UP|DOWN / DONE
每次只输出一个操作。"""
def __init__(self, api_base, api_key, model):
self.client = OpenAI(base_url=api_base, api_key=api_key)
self.model = model
async def analyze(self, screenshot: bytes, instruction: str) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": self.SYSTEM_PROMPT},
{"role": "user", "content": [
{"type": "text", "text": instruction},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64.b64encode(screenshot).decode()}"}}
]}
],
max_tokens=500,
temperature=0.1
)
return response.choices[0].message.content
3.3 完整Web Agent实现
python复制class WebAgent:
def __init__(self, llm_client, browser_manager, page_tagger):
self.llm = llm_client
self.browser = browser_manager
self.tagger = page_tagger
async def execute_task(self, url: str, task: str):
await self.browser.goto(url)
for step in range(20): # 最多20步
# 1. 标注页面元素
screenshot, tag_to_xpath = await self.tagger.tag_page(self.browser.page)
# 2. 让大模型分析并决策
action = await self.llm.analyze(screenshot, task)
# 3. 解析并执行动作
if action == "DONE":
break
await self._execute_action(action, tag_to_xpath)
# 4. 清理标签,等待页面更新
await self.tagger.cleanup(self.browser.page)
4. 高级功能:网站自动化探索
4.1 探索器设计思路
网站自动化探索器的核心流程:
- 访问首页并分析页面结构
- 识别导航菜单和可操作元素
- 按优先级探索各个功能点
- 记录页面变化和操作路径
- 生成网站结构图和测试用例
mermaid复制graph TD
A[访问首页] --> B[分析页面结构]
B --> C[识别可操作元素]
C --> D[执行探索操作]
D --> E[记录页面变化]
E --> F[生成结构图]
4.2 页面分析器实现
python复制class PageAnalyzer:
async def analyze_page(self, screenshot: bytes) -> Dict:
prompt = """分析这个网页截图,返回以下JSON格式的信息:
{
"page_type": "login/home/list/detail/form/dashboard/unknown",
"page_description": "一句话描述这个页面的功能",
"has_sidebar_nav": true/false,
"sidebar_nav_items": ["菜单项列表"],
"suggested_explorations": ["建议探索的操作"]
}"""
response = await self.llm.analyze(screenshot, prompt)
try:
return json.loads(response)
except json.JSONDecodeError:
return {"page_type": "unknown"}
async def analyze_elements(self, screenshot: bytes, context: str) -> List:
prompt = f"""基于当前页面上下文:{context}
分析截图中的可交互元素,返回JSON格式的元素列表:
[{
"tag_id": "元素标签ID",
"semantic_name": "元素语义名称",
"explore_priority": 1-5
}]"""
response = await self.llm.analyze(screenshot, prompt)
try:
return json.loads(response)
except json.JSONDecodeError:
return []
4.3 探索器核心逻辑
python复制class SiteExplorer:
def __init__(self, config, db):
self.browser_manager = BrowserManager(config.browser)
self.page_tagger = PageTagger()
self.page_analyzer = PageAnalyzer(VisionLLMClient(config.llm))
self.db = db
self.visited_urls = set()
self.pending_items = [] # 待探索队列
async def explore_site(self, start_url: str) -> Site:
await self.browser_manager.goto(start_url)
await self._analyze_and_collect_items()
await self._explore_all_items()
return self.site
async def _analyze_and_collect_items(self):
screenshot, _ = await self.page_tagger.tag_page(self.browser_manager.page)
analysis = await self.page_analyzer.analyze_page(screenshot)
# 存储页面信息
page = Page(
url=self.browser_manager.current_url,
page_type=analysis["page_type"],
description=analysis["page_description"]
)
self.db.save_page(page)
# 收集可探索元素
elements = await self.page_analyzer.analyze_elements(screenshot, analysis["page_description"])
for element in elements:
self.pending_items.append(ExploreItem(page, element))
5. 实践经验与问题解决
5.1 常见问题与解决方案
5.1.1 LLM响应不一致问题
问题现象:同一页面,大模型可能返回不同的分析结果
解决方案:
- 实现重试机制
- 添加结果校验
- 提供降级方案
python复制async def analyze_with_retry(self, screenshot, prompt, max_retries=3):
for i in range(max_retries):
result = await self.llm.analyze(screenshot, prompt)
if self._is_valid_response(result):
return result
return {"elements": [], "page_type": "unknown"} # 降级
5.1.2 弹窗处理难题
问题现象:很多操作会触发弹窗而非页面跳转
解决方案:
python复制MODAL_SELECTORS = [".ant-modal", ".el-dialog", ".t-dialog", "[role='dialog']"]
async def handle_modals(self):
for selector in self.MODAL_SELECTORS:
if await self.page.query_selector(selector):
# 执行弹窗关闭操作或处理逻辑
await self.page.click(f"{selector} .close-btn")
return True
return False
5.2 性能优化技巧
- 并行处理:对多个页面的分析可以并行执行
- 缓存机制:缓存已分析过的页面结构
- 智能等待:根据网络状况动态调整等待时间
python复制async def optimized_exploration(self):
tasks = []
for item in self.pending_items[:5]: # 限制并行度
tasks.append(self._explore_item(item))
await asyncio.gather(*tasks)
5.3 安全与稳定性考虑
- 操作限制:设置最大操作步数防止无限循环
- 异常处理:完善各种边缘情况的处理逻辑
- 权限控制:限制可访问的域名范围
python复制class SafetyController:
def __init__(self):
self.max_actions = 200
self.allowed_domains = ["example.com", "test.example.com"]
def check_safety(self, url, action_count):
domain = urlparse(url).netloc
if domain not in self.allowed_domains:
raise Exception("Domain not allowed")
if action_count > self.max_actions:
raise Exception("Max actions exceeded")
6. 应用场景与扩展思考
6.1 典型应用场景
- 自动化测试:自动探索网站并生成测试用例
- 监控巡检:定期检查关键业务流程是否正常
- 数据采集:智能识别和提取页面中的数据
- 无障碍访问:帮助视觉障碍用户浏览网页
6.2 未来扩展方向
- 多模态增强:结合文本和视觉信息提升理解准确率
- 记忆机制:建立网站知识库实现更智能的导航
- 自我优化:通过反馈循环不断改进操作策略
- 跨平台支持:扩展到移动端和桌面应用自动化
6.3 与现有方案的对比
| 特性 | Tarsier方案 | 传统自动化工具 |
|---|---|---|
| 元素定位方式 | 视觉理解 | 硬编码选择器 |
| 维护成本 | 低 | 高 |
| 跨网站复用性 | 优秀 | 差 |
| 处理动态内容能力 | 强 | 弱 |
| 执行速度 | 中等 | 快 |
| 适用场景 | 探索性任务 | 确定性流程 |
在实际项目中,我发现这套方案特别适合处理以下几种情况:
- 页面结构经常变化的项目
- 需要快速验证多个相似网站的场景
- 对代码维护成本敏感的中长期项目
- 需要自动发现网站功能的探索性任务
经过几个月的实践验证,这个工具已经成功应用于我们的多个项目中,将网页自动化相关的开发效率提升了3-5倍,特别是在处理频繁改版的网站时,维护成本降低了80%以上。
