- 后端
- AI 应用
- NLP
【免费下载链接】xberg
Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with CLI, REST API, and MCP server.
Xberg 以 Rust 核心提供了跨语言文档智能提取能力,其中extract的 URL 输入模式(ExtractInputKindURI)支持对远程网页与文档进行抓取。本篇文章聚焦于一个高价值的实战场景——递归文档 URL 提取:当目标页面中通过超链接指向 PDF、TXT、DOCX 等文档资源时,如何让 Xberg 自动跟随这些链接、把“网页 + 其指向的文档”作为多个结果一并提取出来。读完本文,你将掌握 Go 绑定中URLExtractionConfig/CrawlConfig的完整用法(follow_document_urls、document_url_depth、respect_robots_txt等),理解其底层队列式遍历实现,并能通过仓库自带的 fixture 与端到端测试快速验证这套行为。
一、场景:什么是“递归文档 URL 提取”
常规的 URL 提取(URLExtractionModeDocument)只抓取单个远程页面本身,页面上指向其他文档的链接不会继续跟进。而“递归文档链接提取”则把提取范围从单页扩展为以页面为起点的文档链:
- 抓取种子 URL(如
https://example.com)对应的 HTML 页面; - 解析页面中的超链接,识别出其中的文档链接(按扩展名分类,如
.txt、.pdf、.docx等); - 对这些文档链接再次发起抓取,并把文档内容作为独立的提取结果返回。
这一能力在以下场景中非常实用:
- 门户页 / 索引页汇总了多份公告、报表,需要一次性提取全部链接文档;
- CDN 或对象存储上托管文档、由页面间接引用,链接跨域但仍然需要提取;
- 需要将“页面本身”和“页面引用的文档”作为一个批次统一入库。
该能力由配置项follow_document_urls显式开启,属于 opt-in 行为,默认关闭(详见下文源码证据)。
二、完整示例:Go 绑定中的递归提取调用
仓库中的 docs-site/src/snippets-generated/go/url/url_recursive_document_urls.md 提供了一段可直接运行(typecheck 级别、依赖 mock server)的 Go 示例,完整展示了递归提取的最小配置:
package main import ( "fmt" xberg "github.com/xberg-io/xberg/packages/go" ) func ptrT any *T { return &value } func main() { input := xberg.ExtractInput{ Kind: ptr(xberg.ExtractInputKindURI), URI: ptr(`https://example.com`), } config := xberg.ExtractionConfig{ URL: &xberg.URLExtractionConfig{ Mode: ptr(xberg.URLExtractionModeDocument), Crawl: &xberg.CrawlConfig{ RespectRobotsTxt: false, FollowDocumentUrls: true, DocumentURLDepth: ptr(uint32(1)), }, }, } result, err := xberg.Extract(input, config) if err != nil { panic(err) } fmt.Printf("%+v\n", result.Results) fmt.Printf("%+v\n", result.Results[1].Content) }这段代码揭示了递归提取的完整调用链,三个关键点:
- 输入侧:
ExtractInput.Kind必须设置为ExtractInputKindURI,URI给出种子地址(示例中为https://example.com)。在仓库的 fixture 中,种子地址会被替换为 mock server 的地址(见 fixtures/url/url_recursive_document_urls.json 中"uri": "$mock_url"的占位符约定)。 - 模式侧:
URLExtractionModeDocument表示“把 URI 当作单个远程文档/页面处理”,其字符串值即"document"(见 packages/go/binding.go 中URLExtractionModeDocument常量定义)。 - 递归开关:
CrawlConfig.FollowDocumentUrls = true开启文档链接跟进,DocumentURLDepth = 1限定仅跟随一层文档链接,RespectRobotsTxt = false表示不遵守 robots.txt 约束(演示环境下的常见选择)。
运行后,result.Results将至少包含两个元素:Results[0]是种子 HTML 页面的提取结果,Results[1]是页面链接指向的文档(/linked.txt)的提取结果,后者的Content字段包含目标文档正文。示例最后一句fmt.Printf("%+v\n", result.Results[1].Content)正是用于直接验证递归结果的输出语句。
三、配置参数详解:CrawlConfig 核心字段
递归提取完全由URLExtractionConfig.Crawl(类型为CrawlConfig)驱动。在 Go 绑定中,CrawlConfig定义于 packages/go/binding.go,其 JSON 序列化字段与 Rust 侧配置一一对应。以下是与递归文档提取最相关的字段:
| 字段 | JSON 键 | 类型 | 说明 |
|---|---|---|---|
RespectRobotsTxt | respect_robots_txt | bool | 是否遵守 robots.txt。为true时同时遵守页面自身的 robots 指令:不跟随带nofollowrobots meta 标签或X-Robots-Tag头的页面链接;rel="nofollow"仅被视为提示仍会跟随;noindex页面仍会被抓取并在结果中标记noindex_detected |
FollowDocumentUrls | follow_document_urls | bool | 是否把发现的LinkType::Document链接重新入队,从而像跟 HTML 链接一样从文档页(PDF 等)继续向后跟随。默认false——文档在物化(下载)后即终止链路 |
DocumentURLDepth | document_url_depth | *uint32 | 仅统计“经文档链接”的文档深度上限;None表示继承max_depth。它与max_depth相互独立:一个文档 URL 只有同时满足外层max_depth与(若设置)document_url_depth才会入队 |
MaxDepth | max_depth | *uint | 全局最大爬取深度(从种子 URL 出发的链接跳数) |
MaxPages | max_pages | *uint | 最大爬取页数上限 |
MaxConcurrent | max_concurrent | *uint | 最大并发请求数 |
StayOnDomain | stay_on_domain | bool | 页面链接始终被限制在种子域名(可由allow_subdomains放宽到子域);文档链接默认按扩展名分类、可跨主机跟随(常见于 CDN/对象存储场景),置true后文档也要求位于种子域名 |
AllowSubdomains | allow_subdomains | bool | 种子域的子域是否纳入范围 |
DownloadDocuments | download_documents | *bool | 是否下载非 HTML 文档(PDF、DOCX、图片、代码等)而非跳过,默认true |
DocumentMaxSize | document_max_size | *uint | 单个文档下载大小上限,默认 50 MB |
DocumentMimeTypes | document_mime_types | []string | 允许下载的 MIME 类型白名单,为空时使用内置默认 |
SoftHTTPErrors | soft_http_errors | bool | 为true时,404/403/WAF 拦截等 HTTP 错误以ScrapeResult记录(带status_code)形式返回,而非抛错 |
MaxRedirects | max_redirects | *uint | 最多跟随的跳转次数 |
RetryCount/RetryCodes/RetryInitialDelayMs/RetryMaxDelayMs | 同名 JSON | uint / []uint16 / *uint64 / *uint64 | 失败重试次数、触发重试的状态码集合、首次重试延迟(默认 100ms,逐次翻倍)、重试退避上限(默认 60s) |
RateLimitMs | rate_limit_ms | *uint64 | 同域名请求最小间隔,默认 200ms |
IncludePaths/ExcludePaths | 同名 JSON | []string | 爬取路径包含/排除的正则模式 |
UserAgent/CustomHeaders | 同名 JSON | *string / map | 自定义 UA 与请求头 |
其中follow_document_urls与document_url_depth是本次递归提取的核心开关,二者在 Go 绑定中的定义见 packages/go/binding.go:前者负责“是否跟进文档链接”,后者负责“跟进多深”。
四、工作原理解析:从配置到递归队列
递归提取并非简单的“再抓一次”,而是由 Xberg 引擎在提取管线内完成的一轮广度优先遍历。Rust 侧的实现位于 crates/xberg/src/engine/extract_impl.rs 的follow_recursive_document_urls函数,其运行逻辑可以概括为:
- 开关判断:
follow_document_urls(config)读取config.url.crawl.follow_document_urls(extract_impl.rs);若为false直接返回。随后读取document_url_depth(config)(extract_impl.rs),若深度为0同样直接返回。 - URL 模式过滤:如果配置了
document_url_pattern,会将其编译为正则表达式(编译失败会返回invalid document_url_pattern regex校验错误),用于筛选哪些发现的链接属于“文档链接”。 - 入队种子:通过
enqueue_discovered_urls把首次提取结果中发现的文档 URL 以深度 1 放入队列。 - BFS 遍历:循环从队列头部弹出
(uri, depth),每次弹出都会累加output.summary.inputs(结果汇总中的输入计数),然后对该文档 URL 执行抓取与内容提取,再把其内部新发现的文档链接按depth + 1入队——直到队列为空或达到document_url_depth上限。seen集合(HashSet<String>)与seed_hosts集合负责去重与同源约束,避免循环引用导致的无限抓取。
从源码结构可以推断,这一遍历严格遵循广度优先且受双重深度约束:外层max_depth与文档专用的document_url_depth需同时满足才允许文档 URL 入队(Go 绑定注释原文为 “a document URL is enqueued only if BOTH the outermax_depthand (if set)document_url_depthpermit it”)。默认配置下document_url_depth被设为Some(1),即默认最多跟随一层文档链接。
五、默认值与边界行为:default_xberg_crawl_config
Xberg 为 URL 提取内置了一套开箱即用的爬取策略。Rust 侧默认值定义在 crates/xberg/src/core/config/extraction/types.rs 的default_xberg_crawl_config中:
| 字段 | 默认值 |
|---|---|
max_depth | 1 |
max_pages | 100 |
max_concurrent | 10 |
respect_robots_txt | true |
soft_http_errors | true |
stay_on_domain | true |
allow_subdomains | true |
document_url_depth | 1 |
而UrlExtractionConfig层面的默认值(types.rs)还包括:
document_url_pattern: None——不使用自定义文档链接正则,此时文档链接完全依赖扩展名分类;max_document_urls_per_result: Some(100)——单个结果最多携带的文档 URL 数;max_total_urls: Some(1_000)——整个提取过程最多处理的 URL 总数;allow_local_file_inputs: true、allow_file_uris: true——允许本地文件输入与file://URI。
值得注意的边界行为:
- 默认不递归:
follow_document_urls默认false,文档在物化(下载)后即终止链路;递归必须显式开启。 - robots.txt 默认开启:与示例中的
RespectRobotsTxt: false相反,生产默认是遵守 robots.txt 的;在演示/mock 环境关闭它是为了不受外部站点策略干扰。 - 跨主机文档默认允许:文档链接按扩展名分类后再考虑主机,因此默认可以跟随到 CDN 或对象存储上的文档;
stay_on_domain: true会同时把文档约束回种子域名。
六、测试与验证:fixture 与端到端用例
仓库为递归文档提取提供了完整的可复现验证链,包含 mock 服务定义与跨语言 e2e 测试。
6.1 fixture:mock 响应与断言
fixtures/url/url_recursive_document_urls.json 定义了该场景的完整行为契约:
- mock 响应:
GET /返回 HTMLRecursive source页面,内含<a href="/linked.txt">Linked document</a>超链接;GET /linked.txt返回纯文本Recursive document target reached by Xberg.。两个响应均带content-type头(text/html; charset=utf-8与text/plain; charset=utf-8)。 - 输入:
{"kind":"uri","uri":"$mock_url"},其中$mock_url指向 mock server 的 fixture 根路径。 - 配置:
url.mode = "document"、url.crawl.follow_document_urls = true、url.crawl.document_url_depth = 1、url.crawl.respect_robots_txt = false。 - 断言:
not_error:调用不得报错;count_min: results >= 2:结果数至少为 2(页面 + 递归到的文档);contains: results[1].content包含Recursive document target:第二个结果正是被递归抓取的文档正文。
6.2 e2e 测试:Go 侧验证
端到端测试 e2e/go/url_test.go 中的Test_UrlRecursiveDocumentUrls与上述 snippet 严格对应:
inputMockBaseURL := os.Getenv("MOCK_SERVER_URL_RECURSIVE_DOCUMENT_URLS") if inputMockBaseURL == "" { inputMockBaseURL = os.Getenv("MOCK_SERVER_URL") + "/fixtures/url_recursive_document_urls" } inputJSON := strings.ReplaceAll(`{"kind":"uri","uri":"$mock_url"}`, "$mock_url", inputMockBaseURL) // config: {"url":{"crawl":{"document_url_depth":1,"follow_document_urls":true,"respect_robots_txt":false},"mode":"document"}} result, err := xberg.Extract(input, config) // 断言 len(result.Results) >= 2,且 Results[1].Content 包含 "Recursive document target"测试通过环境变量MOCK_SERVER_URL指向 e2e mock 服务,验证了“页面 → 文档链接 → 文档内容”的完整递归链路。该 fixture 同样被其他语言绑定(Rust、Node、Python、Java 等)的 e2e 用例复用,说明follow_document_urls是跨语言一致的稳定契约。
七、实战建议与注意事项
- 深度控制:文档站点的链接图通常较浅,
document_url_depth = 1(跟随一层)多数场景已够用;需要跨多层文档链接时按需递增,并配合max_pages、max_total_urls(默认 1000)防止失控。 - 合规优先:生产环境建议保持
respect_robots_txt: true,并谨慎对待stay_on_domain——页面链接始终被限制在种子域名,文档链接默认允许跨主机(CDN 场景依赖此行为),是否需要收紧取决于你的数据边界要求。 - 错误处理:
soft_http_errors默认开启,404/403 会以带status_code的记录返回而非中断整个提取,适合批量场景;需要严格失败语义时可显式关闭。 - 结合 MCP / CLI 使用:递归文档提取能力经由统一的 Rust 引擎暴露给 CLI、REST API、MCP server 以及全部语言绑定;Go 绑定示例见 packages/go/binding.go,相关生成示例集中在 docs-site/src/snippets-generated/go/url/ 目录(
url_crawl_linked_pages.md、url_html_page_extract.md等同主题示例可对比阅读)。
综上,Xberg 的递归文档 URL 提取通过两个布尔/数值开关(follow_document_urls+document_url_depth)把“抓页面”升级为“抓页面及其引用文档”,其实现基于引擎内的广度优先遍历队列,并配有 fixture 与多语言 e2e 测试保障契约稳定。理解CrawlConfig的字段语义与默认值,即可在各自语言生态中安全、可控地启用这一能力。
- 后端
- AI 应用
- NLP
【免费下载链接】xberg
Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with CLI, REST API, and MCP server.
相关推荐
xberg Dart 实战:用 URL 递归提取配置自动跟随文档链接(fixture 全解析)
xberg Dart 实战:用 URL 递归提取配置自动跟随文档链接(fixture 全解析) 本篇指南围绕 xberg 仓库中 Dart 语言的 url_re
后端AI 应用NLP用 Xberg Dart 绑定提取 DOCX 文档:Smoke Test 代码剖析与底层原理
用 Xberg Dart 绑定提取 DOCX 文档:Smoke Test 代码剖析与底层原理 本篇技术指南围绕 Xberg 仓库中的 Dart 语言 smoke
后端AI 应用NLPXberg 递归文档 URL 提取实战:从入口页面自动发现并追踪文档链接
Xberg 递归文档 URL 提取实战:从入口页面自动发现并追踪文档链接 导读 本文讲解 Xberg 的 递归文档 URL 提取 能力:当以 document
后端AI 应用NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考