1. 项目概述一个能“思考”的浏览器自动化代理如果你也像我一样曾经被那些需要登录、翻页、处理动态加载的网页数据抓取任务折磨到头秃那你一定懂我在说什么。传统的爬虫脚本从requestsBeautifulSoup到Scrapy再到Selenium/Playwright这样的浏览器自动化工具我们一路升级打怪。但痛点始终存在面对复杂的交互逻辑比如需要先点开一个下拉菜单再勾选几个复选框最后点击一个动态生成的按钮我们写的脚本脆弱得像玻璃网页结构一变脚本就崩了面对反爬机制我们得花大量时间研究如何模拟人类行为、管理Cookie、处理验证码。直到我遇到了Crawlio Browser Agent。这玩意儿初看名字我以为又是一个基于Playwright或Puppeteer的封装库。但深入研究后我发现它的野心远不止于此。它不是一个简单的“自动化脚本运行器”而是一个具备一定自主决策能力的浏览器智能体Agent。它的核心思想是将你的抓取目标比如“抓取这个电商网站前5页所有商品的名称、价格和评论数”用自然语言或结构化指令描述出来然后由这个Agent去理解任务自主规划操作步骤点击、输入、滚动、等待并从中提取数据。它试图让爬虫从“死板执行预设命令”进化到“根据目标动态调整策略”。简单来说Crawlio Browser Agent 试图解决的是复杂、动态、交互式网页的稳健抓取问题。它适合那些需要模拟真实用户操作流程才能获取数据的场景比如需要登录的社交平台、依赖大量JavaScript渲染的单页应用SPA、操作步骤繁琐的企业后台系统等。对于数据工程师、分析师、以及任何需要从现代Web应用中稳定获取数据的开发者来说这无疑是一个极具吸引力的新工具。2. 核心架构与设计哲学拆解要理解Crawlio Browser Agent我们不能只把它当工具用得先看看它肚子里装的是什么“引擎”。它的设计哲学深刻影响了它的能力和边界。2.1 基于大语言模型LLM的任务理解与规划这是Crawlio区别于传统爬虫框架最根本的一点。传统爬虫是过程式的开发者必须精确地告诉程序“先访问A URL然后解析出B元素的href再访问那个链接接着在C输入框填入关键词最后点击D按钮”。每一步都写死在代码里。而Crawlio Browser Agent 引入了声明式与智能规划的结合。你给它的输入更接近于任务目标例如一个结构化的指令{ goal: 从示例电商网站https://demo-store.com的‘笔记本电脑’类别下获取前3页的产品列表包括产品名称、价格和库存状态。, start_url: https://demo-store.com/categories/laptops }或者在未来更成熟的版本中可能直接使用自然语言“帮我看看XX网站上的笔记本电脑前三页的都要名字、价钱和有没有货。”Agent内部的大语言模型如GPT-4、Claude 3等会负责解析这个目标。它会将目标分解成一系列原子操作形成一个执行计划。这个计划可能包括导航到起始URL。识别并可能点击“下一页”按钮如果第一页内容不足。识别页面中的产品列表容器。对于列表中的每个产品项定位并提取名称、价格、库存状态对应的HTML元素。将数据整理成结构化格式如JSON、CSV。这个规划过程是动态的。如果点击“下一页”失败比如按钮的CSS选择器变了LLM可以基于当前的页面状态和错误信息重新规划策略例如尝试滚动加载或寻找分页器的其他表现形式。2.2 分层执行引擎连接“思考”与“行动”光会“想”不行还得会“做”。Crawlio Browser Agent 的架构通常包含以下层次Orchestrator协调层这是大脑。它持有任务目标调用LLM进行任务分解和步骤规划。它接收来自执行层的反馈成功/失败、当前页面状态并决定下一步是继续执行原计划还是需要重新规划。Action Executor动作执行层这是小脑和手。它接收协调层发出的具体动作指令如click(selector“.next-page-btn”)、extract_text(selector“.product-name”)。这一层与底层的浏览器自动化驱动如Playwright进行交互将抽象指令转化为具体的浏览器API调用。Browser Driver浏览器驱动层这就是我们的老朋友Playwright或Puppeteer。它提供最基础的浏览器控制能力打开页面、查找元素、模拟点击、输入文本、执行JavaScript等。Crawlio Browser Agent 通常深度集成其中一个以利用其稳定性和丰富的功能。State Observer Extractor状态观察与提取层这是眼睛。它持续监控浏览器页面的状态。这不仅包括获取页面HTMLpage.content()更关键的是获取可访问性树Accessibility Tree和语义信息。LLM可以更好地理解“这是一个按钮上面写着‘加载更多’”而不是“这是一个div类名是js-load-more”。同时这一层也负责根据指令从当前页面中提取目标数据。这个分层架构的关键在于闭环反馈执行动作 → 观察结果状态 → 与预期对比 → 决定下一步。这使得Agent具备了处理异常和不确定性的基础能力。2.3 上下文管理Context Management与记忆Memory对于一个需要执行多步骤任务的Agent来说上下文管理至关重要。它需要记住任务目标最终要达成什么。已执行步骤已经做了什么避免循环操作。已收集数据已经抓取到了哪些信息。当前页面状态URL、主要的页面内容摘要等。Crawlio Browser Agent 会维护一个任务上下文通常以会话Session或对话Conversation的形式存在。LLM在每一步决策时都会接收到这个完整的上下文从而做出更连贯的决策。例如当它发现点击一个按钮后页面没有跳转而是动态加载它会更新上下文“当前处于列表页已点击‘加载更多’列表已更新需要继续提取新出现的项目。”注意上下文长度是LLM应用的经典限制。Crawlio需要设计巧妙的上下文窗口管理策略比如只保留最近N步的详细操作和关键的页面摘要而不是把整个浏览历史都塞进去否则很快就会触及LLM的Token上限。3. 关键技术与实操要点解析理解了架构我们来看看在具体使用和开发类似Agent时有哪些技术细节是成败的关键。3.1 动作空间Action Space的设计Agent能执行的动作是有限的这构成了它的“动作空间”。一个设计良好的动作空间需要兼顾表达能力和可控性。基础导航动作goto(url),go_back(),go_forward(),reload()元素交互动作click(selector),hover(selector),fill(selector, text),select_option(selector, value)页面操作动作scroll(direction, amount),wait_for_selector(selector),wait_for_time(ms)信息提取动作get_text(selector),get_attribute(selector, attr),screenshot(),extract_data(schema)根据预定义模式提取判断与决策动作if_element_exists(selector),compare_text(selector, expected_value)这些可能作为规划的条件而非直接执行的动作在Crawlio的实现中这些动作会被定义成LLM可以理解和生成的格式比如函数调用Function Calling的格式。LLM的输出不再是自然语言而是一个结构化的动作调用指令。实操心得动作设计并非越多越好。过于复杂的动作会增加LLM理解和规划的难度。初期应从最小可用集开始例如click,fill,extract_text,goto确保这些动作在底层驱动中稳定可靠。对于复杂的提取操作可以设计一个强大的extract_data动作它接收一个JSON Schema作为参数告诉Agent需要提取哪些字段以及对应的CSS选择器或XPath这样比让LLM自己猜测选择器要稳健得多。3.2 元素定位与描述的稳健性这是传统自动化测试和爬虫的老大难问题在Agent场景下有了新思路但挑战依旧。传统方式的局限依赖固定的CSS选择器或XPath。页面微调就会导致失败。Agent的增强方式语义描述让LLM根据元素的视觉特征、附近文本、可访问性标签aria-label来定位。例如“点击那个蓝色的、写着‘提交订单’的按钮”。这要求动作执行层能将这种描述转化为实际的选择器可能需要结合计算机视觉CV或更高级的DOM分析。多模态LLM输入将页面截图和DOM信息同时喂给LLM让它“看到”页面并指出要操作的元素。这是目前最前沿也最有效的方式但成本较高。混合策略推荐在任务配置中允许用户为关键元素如登录按钮、搜索框、分页器提供备选选择器列表或语义描述。Agent优先尝试用户提供的精确选择器失败后再回退到让LLM基于语义寻找。Crawlio Browser Agent 很可能采用了类似的混合策略来平衡精度与鲁棒性。避坑指南对于登录、提交等关键操作步骤强烈建议在任务配置中显式指定元素选择器。完全依赖LLM的“视觉”定位在复杂或动态页面上仍有失败风险。把LLM的智能用在处理非预期的弹窗、识别新的内容加载方式等“模糊”问题上而不是所有元素的定位上。3.3 等待与状态判断策略“点击后等多久”这是自动化脚本中最常见的难题。笨办法是固定等待page.wait_for_timeout(5000)这既低效又不稳定。Crawlio这类智能Agent应该有更聪明的等待策略预期结果等待在动作指令中定义“成功状态”。例如click(selector“.submit”, expected_result”url_changed” OR “selector_appeared(“.success-message”)”)。执行层在点击后会持续监测页面直到预期状态出现或超时。LLM辅助的状态判断动作执行后将新的页面摘要或关键区域截图发给LLM询问“点击登录按钮后页面是否成功跳转到了用户主页”LLM可以根据页面内容做出判断。这比硬编码的规则灵活得多。网络空闲检测结合Playwright的wait_for_load_state(‘networkidle’)等待页面主要网络请求完成这对于SPA应用很有效。实操配置示例在你的爬虫任务定义中可以为每一步配置等待策略。steps: - action: click selector: #loadMoreButton wait_for: type: selector value: .product-item:nth-child(10) # 等待第10个商品项出现假设一页9个 timeout: 10000这告诉Agent点击加载更多按钮后不要傻等而是持续检查是否出现了新的商品项比如第10个最多等10秒。4. 从零开始构建一个简易爬虫智能体理解了原理我们不妨动手设计一个简化版的Crawlio核心逻辑。这将使用Python、Playwright和OpenAI API或其他兼容的LLM API来演示。请注意这是一个概念验证原型真实的Crawlio项目要复杂和健壮得多。4.1 环境准备与依赖安装首先确保你的环境已就绪。# 创建项目目录并进入 mkdir simple-web-agent cd simple-web-agent # 创建虚拟环境推荐 python -m venv venv # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate # 安装核心依赖 pip install playwright openai python-dotenv # 安装Playwright的浏览器内核 playwright install chromium我们使用python-dotenv来管理敏感的API密钥。在项目根目录创建.env文件OPENAI_API_KEY你的OpenAI_API密钥 OPENAI_BASE_URL你的API基础地址如果使用第三方兼容服务4.2 定义智能体核心类我们创建一个simple_agent.py文件开始构建智能体的骨架。import asyncio from typing import List, Dict, Any, Optional import json from openai import AsyncOpenAI from playwright.async_api import async_playwright, Page, BrowserContext import os from dotenv import load_dotenv load_dotenv() # 加载环境变量 class SimpleWebAgent: def __init__(self, model: str gpt-4o-mini): 初始化智能体。 :param model: 使用的LLM模型名称。 self.client AsyncOpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL, None) # 兼容其他兼容OpenAI API的服务 ) self.model model self.context: Optional[BrowserContext] None self.page: Optional[Page] None self.playwright None self.browser None # 存储任务历史作为LLM的上下文 self.history: List[Dict[str, Any]] [] async def start(self): 启动Playwright浏览器实例。 self.playwright await async_playwright().start() # 使用带图形界面的浏览器便于调试生产环境可改用 headlessTrue self.browser await self.playwright.chromium.launch(headlessFalse, slow_mo100) self.context await self.browser.new_context(viewport{width: 1280, height: 720}) self.page await self.context.new_page() async def stop(self): 关闭浏览器实例。 if self.browser: await self.browser.close() if self.playwright: await self.playwright.stop() async def _call_llm(self, system_prompt: str, user_prompt: str) - Dict[str, Any]: 调用LLM期望返回一个结构化的动作指令。 messages [ {role: system, content: system_prompt}, {role: user, content: user_prompt} ] # 我们要求LLM以JSON格式返回 response await self.client.chat.completions.create( modelself.model, messagesmessages, response_format{type: json_object}, # 要求返回JSON temperature0.1, # 低随机性保证动作稳定 ) content response.choices[0].message.content try: return json.loads(content) except json.JSONDecodeError: print(fLLM返回了非JSON内容: {content}) # 简单回退尝试提取可能的动作 return {action: fail, reason: LLM response not valid JSON} async def execute_task(self, goal: str, start_url: str): 执行一个爬虫任务。 :param goal: 任务目标描述。 :param start_url: 起始URL。 print(f开始任务: {goal}) await self.page.goto(start_url) await self.page.wait_for_load_state(networkidle) # 初始系统提示词定义智能体的角色和能力 system_prompt 你是一个网页自动化智能体。你的目标是通过操作浏览器来完成用户指定的任务。 你可以执行以下动作请以JSON格式回复格式如下 { action: 动作名称, parameters: { ... } // 动作参数 } 可用动作 1. click: 点击一个元素。 参数: {selector: CSS选择器} 2. fill: 向输入框填充文本。 参数: {selector: CSS选择器, text: 要输入的文本} 3. extract: 从当前页面提取信息。 参数: {schema: [{field_name: 字段名, selector: CSS选择器}]} 4. scroll: 滚动页面。 参数: {direction: down 或 up, amount: many 或 little} 5. wait: 等待。 参数: {condition: selector 或 time, value: 选择器或毫秒数} 6. done: 任务完成。 参数: {data: 提取到的数据} 或 {reason: 完成原因} 请根据当前任务目标和页面情况决定下一步做什么。保持步骤简单直接。 task_in_progress True collected_data [] while task_in_progress: # 1. 获取当前页面关键信息作为LLM的“观察” # 简化获取页面标题和前500个字符的主要内容实际可以更复杂如截图、关键区域HTML page_title await self.page.title() # 获取body的可见文本简单处理 page_content await self.page.evaluate(() { return document.body.innerText.substring(0, 1500); }) # 构建给LLM的用户提示 user_prompt f 任务目标: {goal} 当前页面标题: {page_title} 当前页面内容预览: {page_content} 历史操作: {json.dumps(self.history[-5:], ensure_asciiFalse)} // 只提供最近5步历史 请分析当前情况并决定下一步动作。只返回JSON。 # 2. 调用LLM获取动作指令 llm_response await self._call_llm(system_prompt, user_prompt) print(fLLM决策: {llm_response}) action llm_response.get(action) params llm_response.get(parameters, {}) # 3. 执行动作 result {action: action, params: params, success: False, result: None} try: if action click: selector params.get(selector) if selector: await self.page.click(selector) await self.page.wait_for_load_state(networkidle, timeout5000) result[success] True result[result] f成功点击: {selector} else: result[result] 缺少选择器参数 elif action fill: selector params.get(selector) text params.get(text) if selector and text is not None: await self.page.fill(selector, text) result[success] True result[result] f成功在 {selector} 填入: {text} else: result[result] 缺少选择器或文本参数 elif action extract: schema params.get(schema, []) extracted {} for item in schema: field item.get(field_name) selector item.get(selector) if field and selector: try: # 简单提取文本可扩展为属性等 value await self.page.text_content(selector) extracted[field] value.strip() if value else except Exception as e: extracted[field] f提取失败: {e} collected_data.append(extracted) result[success] True result[result] extracted elif action scroll: direction params.get(direction, down) amount params.get(amount, many) scroll_px 800 if amount many else 200 if direction down: await self.page.evaluate(fwindow.scrollBy(0, {scroll_px})) else: await self.page.evaluate(fwindow.scrollBy(0, -{scroll_px})) await asyncio.sleep(1) # 等待滚动后内容加载 result[success] True result[result] f向{direction}滚动{amount} elif action wait: condition params.get(condition) value params.get(value) if condition selector: await self.page.wait_for_selector(value, timeout10000) result[success] True result[result] f等待到选择器出现: {value} elif condition time: await asyncio.sleep(int(value) / 1000) result[success] True result[result] f等待了{value}毫秒 elif action done: task_in_progress False result[success] True final_data params.get(data, collected_data) result[result] {status: 任务完成, collected_data: final_data} print(f任务完成收集到的数据: {final_data}) else: result[result] f未知动作: {action} except Exception as e: result[result] f执行动作时出错: {e} # 4. 记录历史 self.history.append(result) print(f动作执行结果: {result}) # 简单防呆防止无限循环 if len(self.history) 20: print(步骤过多强制终止任务。) break return collected_data # 主函数 async def main(): agent SimpleWebAgent(modelgpt-4o-mini) # 可根据实际情况选择模型 try: await agent.start() # 示例任务在DuckDuckGo搜索并提取第一条结果的标题 goal 在DuckDuckGo搜索playwright python并提取第一页第一条搜索结果的标题和链接。 start_url https://duckduckgo.com/ data await agent.execute_task(goal, start_url) print(最终数据:, data) finally: await agent.stop() if __name__ __main__: asyncio.run(main())这个简易Agent展示了核心流程启动浏览器 → 观察页面 → LLM决策 → 执行动作 → 记录反馈 → 循环。它非常基础但清晰地勾勒出了Crawlio Browser Agent 的工作范式。5. 生产环境部署与优化考量如果要将这样的智能体用于实际生产数据抓取我们面临的挑战会急剧增加。以下是一些关键的优化方向。5.1 性能、成本与稳定性平衡LLM调用成本与延迟每一步都调用GPT-4无疑成本高昂且慢。策略包括缓存对相同的页面状态和任务目标缓存LLM的决策结果。本地小模型对于简单的、模式化的操作如“翻到下一页”可以训练或使用一个轻量级模型如经过微调的BERT类模型来决策仅在复杂、不确定的场景下调用大模型。批量规划让LLM一次规划多个步骤而不是一步一询。例如“识别出所有产品项并生成提取每个项的指令序列”。浏览器实例管理每个任务一个浏览器实例开销巨大。需要实现浏览器实例池并妥善管理上下文Cookies、LocalStorage的隔离与复用。并发与速率限制控制同时运行的Agent数量遵守目标网站的robots.txt和服务条款添加合理的请求间隔避免被封IP。5.2 错误处理与自我修复能力一个健壮的Agent必须能处理各种异常。动作执行失败点击的元素不存在、页面跳转异常、网络超时。Agent需要能捕获这些异常并将其作为“负面观察”反馈给LLM让LLM重新规划。例如“点击登录按钮失败按钮可能被遮挡或选择器已失效请尝试其他方式定位登录入口。”偏离预期路径点击一个链接后可能跳转到了广告页、错误页或登录页。Agent需要有能力检测当前页面是否仍在正确的任务流中。这可以通过LLM判断页面内容或预定义一些“检查点”如特定关键词必须出现在页面标题中来实现。无限循环检测如上文代码所示需要记录步骤历史当检测到重复或循环模式时如反复在同一个页面点击同一个无效按钮应主动终止任务并报错。5.3 可观测性Observability与调试当拥有成百上千个智能体在运行时监控和调试变得至关重要。详细日志记录每一个LLM的输入页面摘要、输出动作决策、执行结果和新的页面状态。这些日志是事后分析任务失败原因的黄金资料。屏幕录制与快照自动录制任务执行过程的视频或在关键步骤决策前后、错误发生时截取屏幕截图。这对于调试复杂的交互问题无可替代。仪表盘需要一个中央仪表盘来查看所有运行中/已完成任务的状态、成功率、耗时、LLM调用次数和成本等指标。6. 典型应用场景与实战案例解析Crawlio Browser Agent 这类技术并非万能但在特定场景下优势明显。6.1 场景一复杂单页应用SPA数据抓取挑战内容全部由JavaScript动态加载和渲染没有完整的页面刷新。传统爬虫难以触发加载事件和解析动态内容。Agent方案导航到SPA的初始URL。Agent通过LLM“观察”页面识别出数据加载的触发机制如“滚动加载更多”、“点击‘加载更多’按钮”。Agent模拟用户滚动或点击触发数据加载。每次新内容加载后Agent提取数据并判断是否已满足任务要求如“已收集100条项目”或“‘加载更多’按钮消失”。LLM的动态规划能力可以处理SPA中可能出现的非标准交互比如一个可无限滚动的虚拟列表。6.2 场景二需要多步骤登录与导航的后台系统挑战数据位于需要登录的企业后台登录流程可能涉及多因素认证MFA且数据分布在层层菜单之后。Agent方案在任务配置中预先提供登录凭证和关键导航步骤的强引导如登录按钮的精确选择器、MFA输入框的描述、主导航菜单的文本。Agent按步骤执行登录。遇到MFA时可以配置为暂停并等待用户手动输入或集成短信/邮箱令牌自动获取服务需谨慎考虑安全。登录后Agent根据任务描述如“进入‘报表中心’-‘销售明细’-选择日期‘2024-01-01’至‘2024-03-31’-导出CSV”逐步导航到目标页面。在目标页面执行数据提取或触发导出操作。LLM可以处理导航过程中可能出现的意外弹窗或提示信息。6.3 场景三竞争对手网站内容与价格监控挑战竞争对手网站频繁改版选择器经常失效。价格信息可能通过图片、Canvas或复杂CSS呈现难以直接提取。Agent方案定义监控目标产品URL、需要抓取的字段价格、库存、促销信息。Agent访问产品页。LLM的视觉/语义理解能力可以帮助定位价格区域即使它的HTML结构变了。例如LLM可以识别出“$199.99”这个文本在页面中的位置并关联其附近的“Add to Cart”按钮来确认这是价格。对于Canvas或图片价格可以结合OCR光学字符识别模块。Agent负责将页面截图或特定区域截图发送给OCR服务再将结果整合。由于LLM具有一定的泛化能力当网站进行小幅改版时Agent可能无需修改配置就能自适应大大降低了维护成本。实战心得在这些场景中最大的价值在于降低维护成本和处理不确定性。传统爬虫需要工程师持续跟进网站变化。而一个训练有素的Agent其核心能力理解目标、规划步骤、处理异常是通用的只需在关键节点如登录、核心数据定位给予一些提示或备用方案就能在较长时间内稳定工作。当然它并非“零维护”当网站发生巨大改版时仍然需要人工介入调整任务描述或提供新的示例。7. 常见问题、局限性与未来展望7.1 当前局限性成本频繁调用大模型API费用不菲是规模化应用的主要障碍。速度每一步都需要LLM推理相比静态爬虫慢几个数量级。不适合需要海量、高速抓取的场景。可靠性LLM的决策并非100%可靠可能出现“幻觉”比如点击一个不存在的按钮或做出低效的规划。需要多层校验和回退机制。复杂交互对于需要拖拽、绘图、处理复杂验证码如扭曲文字、行为验证的交互纯基于LLM的Agent目前仍力不从心需要集成专门的CV或RPA模块。道德与法律风险自动化抓取必须严格遵守网站的robots.txt和服务条款。使用Agent进行大规模抓取可能引发法律纠纷。它更像是一个强大的“自动化助手”应在合规和伦理的框架内使用。7.2 调试与问题排查技巧当你发现Agent卡住或行为异常时可以按以下步骤排查检查日志首先查看每一步的LLM输入页面摘要和输出动作决策。页面摘要是否准确反映了关键信息LLM的决策是否符合逻辑可视化执行过程回放屏幕录制或查看关键节点的截图。确认页面是否按预期渲染元素是否可见、可交互。简化任务将复杂任务拆解成更小的子任务逐一测试。例如先测试“能否成功登录”再测试“登录后能否导航到目标页面”。增强提示词Prompt如果LLM总是误解某个指令尝试在系统提示词或用户提示词中提供更明确、更详细的约束和示例。例如“在寻找‘搜索框’时优先寻找input类型为search或text且placeholder属性包含‘搜索’字样的元素。”提供备用选择器在任务配置中为关键元素提供多个备选选择器CSS、XPath、文本内容提高定位的鲁棒性。检查网络与资源确保目标网站可访问没有触发Cloudflare等反爬机制。检查Playwright是否成功加载了所有必要的资源图片、CSS、JS。7.3 未来演进方向Crawlio Browser Agent 代表了一个趋势将大模型的认知能力与传统自动化的执行能力相结合。它的未来可能围绕以下几点演进多模态能力深度融合结合视觉模型VLM让Agent真正“看到”网页像人一样理解图标、布局和视觉状态彻底摆脱对DOM结构的依赖。小模型与专用模型针对网页交互训练专用的小型、高效模型用于处理常见的模式化操作将大模型作为“指挥官”处理复杂决策以降低成本、提高速度。学习与自适应Agent能够从成功和失败的历史任务中学习自动更新其对特定网站交互模式的理解甚至能生成可复用的“操作脚本”或“适配器”。与RPA流程集成不仅操作浏览器还能与桌面应用、API、数据库交互成为真正的端到端业务流程自动化智能体。从我个人的实践来看Crawlio Browser Agent 及其所代表的技术路径正在将我们从“爬虫工程师”的角色中部分解放出来让我们更专注于定义“抓取什么”和“为什么抓取”而将“如何抓取”的复杂实现交给更智能的系统。它不会完全替代传统爬虫因为后者在简单、静态、大规模的抓取任务上仍有不可比拟的效率优势。但它为攻克那些曾经需要大量定制化开发和维护的“动态网页抓取堡垒”提供了一把锋利的新武器。