feat: add recursive web crawler MVP with SPA support and fine-grained limits for add-resource#2424
feat: add recursive web crawler MVP with SPA support and fine-grained limits for add-resource#2424sponge225 wants to merge 5 commits into
Conversation
# Conflicts: # openviking/server/mcp_endpoint.py
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
Description
本 PR 实现了递归网页爬取 MVP,使
add_resource/add-resource能够从一个网页入口出发,继续发现并导入其子页面内容。该能力主要面向文档站、知识库站点和单页应用(SPA/CSR)页面,支持通过 Playwright 渲染动态页面,并提供基础的爬取范围控制、URL 去重、重定向去重、网络目标校验和资源组织能力。本次改动的目标不是做一个完整通用爬虫,而是交付一个安全、可控、可 review 的 MVP:默认行为尽量保守,避免无限爬取、重复抓取、资源树结构混乱或对目标站点造成过高压力。
核心能力概览
depth、max_pages、include_paths、exclude_paths、allow_external_links、use_playwright控制爬取行为。depth=-1表示不限制递归深度,同时仍受max_pages、URL 去重和过滤规则约束。final_url去重,避免入口 URL 或子页面 301/302 后重复导入同一页面。viking://resources根目录。--args参数,用于向add_resource透传 parser-specific / import-specific 参数。默认资源组织策略
当用户导入 HTTP(S) URL 且没有显式指定
to/parent时,系统会默认使用 URL 域名作为父目录,并自动创建该目录。例如:
默认会写入:
这样可以避免多个不同站点或多个 URL 直接混在:
如果用户显式指定
--parent、--parent-auto-create或to,系统会尊重用户指定的位置,不会覆盖为域名目录。add-resource参数说明当前 CLI 网页递归爬取参数统一通过
--args传递:--args='depth:1,max_pages:30,use_playwright:true'支持的主要参数如下:
depth(int):爬取深度。0表示仅导入当前页面;1表示导入当前页面及其直接子链接;以此类推。特殊值-1表示不限制递归深度,直到达到max_pages或无更多可抓取页面。max_pages(int):本次递归爬取的全局页面上限,默认100。该限制控制整个爬取任务最多调度多少个页面请求,防止无限递归或大站点导致资源失控。include_paths(str):路径白名单,支持 Glob 模式。例如/docs/84313/*。设置后,只有路径匹配白名单的页面才会被抓取。exclude_paths(str):路径黑名单,支持 Glob 模式。例如*.pdf或*/login*。匹配黑名单的页面会被跳过。allow_external_links(bool):是否允许抓取外部域名链接。默认False,即只抓取与入口 URL 同域名的页面,避免爬虫扩散到无关站点。use_playwright(bool):是否使用 Playwright 无头浏览器渲染页面。默认开启,用于支持 SPA/CSR 页面;如果目标站点是纯静态 HTML,可设为False以提升速度。CLI
--args设计说明本次 CLI 不再提供
--depth、--max-pages、--include-paths、--exclude-paths、--allow-external-links等网页爬取专用顶层参数,统一改为:设计原因:
add_resource底层会逐步支持更多 parser-specific / import-specific 参数,如果全部暴露为 CLI 顶层 flag,会导致 CLI 参数膨胀。--args允许 CLI 以更通用的方式向后端透传扩展参数,后续新增解析器参数时不需要频繁修改 CLI 顶层接口。add-resource的核心参数仍聚焦于资源导入位置、上传、等待和通用处理逻辑。--args支持基础类型解析:true/false会被解析为 boolean。null会被解析为 null。--args的分隔规则:--args='depth:-1,max_pages:25,use_playwright:true'--args='include_paths:"/docs/84313/*,/docs/84314/*",exclude_paths:"*.pdf,*/login*"'include_paths = "/docs/84313/*,/docs/84314/*"exclude_paths = "*.pdf,*/login*"include_paths/exclude_paths中的多个 Glob 模式。Usage Examples
CLI 示例
MCP / JSON 参数示例
MCP / API 调用可以直接传递结构化参数:
{ "path": "https://www.volcengine.com/docs/84313", "depth": -1, "max_pages": 25, "include_paths": "/docs/84313/*,/docs/84314/*", "exclude_paths": "*.pdf,*/login*", "allow_external_links": false, "use_playwright": true }如果需要自定义父目录,也可以显式传:
{ "path": "https://www.volcengine.com/docs/84313", "parent": "viking://resources/volcengine-docs", "depth": 1, "max_pages": 30, "use_playwright": true }资源结构示例
默认不传父目录时:
显式指定父目录时:
不同站点默认会分到不同域名目录下:
核心设计说明
_pages_scheduled记录已经调度的页面数,在发起请求前检查max_pages,避免高并发场景下超额调度。depth=-1只是不限制层级,不代表无限请求;实际仍受max_pages、URL 去重和过滤规则约束。request_validator,HTTP 抓取使用 httpx hooks 校验请求,Playwright 抓取通过 route 拦截校验页面加载请求,并在最终 URL 上再次校验,避免子链接绕过公共目标限制。include_paths、exclude_paths和allow_external_links控制爬取边界,避免导入无关页面。url1和url2是平行资源,符合“每个 URL 都可能拥有独立文档结构”的使用模型。to/parent时,系统默认使用 URL 域名作为父目录,并自动创建该目录,避免多个站点平铺到viking://resources根目录。--args通用参数,将 parser-specific import options 解析为 JSON 字段后合并进add_resource请求体,减少未来 CLI 顶层参数膨胀。MVP 隐藏限制与已知边界
为了保证 MVP 版本安全可控,当前实现中包含一些保守的内置限制和启发式规则。它们可能影响用户实际使用效果,因此在 PR 中显式说明:
CrawlConfig.max_links_per_page当前默认固定为50。即使某个页面包含大量合法内链,单个页面最多只会向队列追加前 50 个链接。这样可以避免 sitemap、导航页、页脚聚合链接瞬间填满队列并耗尽max_pages。本 PR 已确保只有真正进入队列的 URL 才会写入 visited,避免超限链接被错误去重。后续应考虑将该上限暴露为可配置参数,或引入链接优先级/正文区域识别策略。domcontentloaded后会额外等待约2000ms,用于给前端框架留出渲染时间。这提升了动态页面抓取成功率,但会显著降低抓取速度。后续可优化为可配置等待时间或基于网络空闲/选择器的智能等待。<a>链接。后续可考虑 SSR 与 DOM 链接合并。#fragment。例如/docs/a#section1和/docs/a#section2会被视为同一页面。这符合多数文档页的语义,但如果站点使用 hash routing,可能需要后续增强。CrawlConfig.concurrency默认值为5,当前没有通过add-resource参数暴露。这样默认更安全,但无法让高级用户按机器资源或目标站点能力调优。add_resource链路:当前 MVP 在抓取到子页面后,仍复用现有add_resource流程逐个导入子页面,而不是专门的 batch persist。这样可以最大化复用现有解析、写入、索引能力,但还不是最终架构。后续建议演进为crawl -> batch persist -> batch index。--args表达能力仍较轻量:当前--args支持顶层逗号分隔和双引号包裹含逗号 value,可以满足常见多参数与多路径模式场景;但相比完整 JSON 文件或重复参数形式,复杂嵌套配置的可读性仍有限,后续可继续优化。Related Issue
Type of Change
Changes Made
depth、max_pages、include_paths、exclude_paths、allow_external_links、use_playwright等网页爬取控制参数。depth=-1表示不限制递归深度,并通过max_pages作为全局安全上限。--args通用参数,用于向add_resource透传 parser-specific / import-specific 参数。add-resource网页爬取选项统一通过--args传递。--args解析能力,支持 value 中包含逗号的多路径模式,例如include_paths:"/a/*,/b/*"。final_url识别,并将其作为 canonical URL 用于子页面去重和写入。to/parent时,HTTP(S) URL 默认导入到viking://resources/<domain>/。max_pages调度计数,避免高并发下超过用户设置的页面上限。max_links_per_page与 visited 的顺序,避免超出单页上限的链接被错误标记为已访问。--args增加解析测试,覆盖正常解析、包含逗号的 value、非法格式报错以及旧顶层爬取 flag 被拒绝。Testing
已验证场景:
depth和max_pages能限制递归深度与全局页面数量。depth=-1可通过 CLI--args='depth:-1,...'正常传入。include_paths能限制只抓取指定路径范围内的页面。exclude_paths能排除 PDF、登录页等不需要导入的路径。allow_external_links=false时不会跨域抓取外部站点。use_playwright=true时可以获取渲染后的页面内容。depth=-1下无限递归,因为 URL visited 去重会拦截重复 URL。to/parent的 HTTP(S) URL 会默认写入viking://resources/<domain>/。ov add-resource --help已展示--args <key:value,...>,不再展示旧的网页爬取专用 flag。--depth、--max-pages。--args单元测试覆盖了depth、max_pages、allow_external_links、include_paths等字段解析。python3 -m compileall openviking/service/resource_service.py通过。python3 -m compileall openviking/utils/crawl_filter.py openviking/utils/page_fetcher.py openviking/utils/web_crawler.py openviking/service/resource_service.py通过。cargo check -p ov_cli通过。cargo test -p ov_cli add_resource通过。cargo test -p ov_cli legacy_web_crawl_flags通过。ov add-resource --help已显示新的--args用法。Checklist
Screenshots (if applicable)
Additional Notes
本 PR 当前定位为 Web Crawler MVP。后续建议继续优化以下方向:
max_links_per_page、concurrency、Playwright 等待时间等内置参数暴露为高级配置。--args表达能力,例如支持 JSON 文件、重复参数或更完整的转义规则,方便传递复杂配置。depth=-1、循环链接、跨域控制和默认父目录策略。add_resource”演进为crawl -> batch persist -> batch index的专用流程,实现稳定 URL-to-URI 映射和批量索引。