- 后端
- 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.
递归 URL 提取(recursive URL extraction)是 Xberg URL 摄取能力中的一项进阶特性:当以document模式抓取一个网页时,引擎会从提取结果中发现的文档链接(PDF、TXT、DOCX 等)继续向下抽取,形成一条"网页 → 文档 → 文档内链接 → …"的递归链路。本文以 url_recursive_document_urls.md 中的 Kotlin(Android)示例为主线,讲解如何用Xberg.extract打开递归文档链接抓取、如何通过CrawlConfig.follow_document_urls与document_url_depth控制递归深度与边界,并结合仓库源码说明其底层实现原理。读完本文,你将能在自己的 Android/JVM 工程中直接落地"一个 URL 一次调用、递归收获整条文档链"的提取方案。
说明:文中的 Kotlin 示例来自 alef 自动生成的 e2e 夹具(fixture),其完整定义位于 url_recursive_document_urls.json,对应的端到端测试用例见 UrlTest.kt。
场景:一次调用拿到"页面 + 文档链"
官方夹具对这条特性的一句话概括是:
extract: recursive URL extraction follows document links discovered in results
翻译过来即:递归 URL 提取会跟随提取结果中发现的文档链接。典型使用场景是:
- 一个 HTML 首页上列出了一批下载链接(
.pdf、.txt、.docx…),你想把首页正文和所有链接指向的文档内容一并提取出来; - 一个文档(如 PDF)内部又引用了其他文档,需要沿引用链继续抓取;
- 站点资源分散在不同 CDN 域名上,文档链接默认允许跨主机跟随(见下文
stay_on_domain的说明)。
在 Xberg 中这不需要写任何爬虫循环——document模式加上递归开关即可,引擎内部会自动把"结果中发现的新 URL"重新投喂给提取管线。
完整可运行的 Kotlin(Android)示例
夹具生成的 Kotlin 代码形态如下,它同时演示了 Jackson(snake_case)反序列化、ExtractInput与ExtractionConfig的 JSON 构造,以及Xberg.extract的调用与结果访问:
import io.xberg.* import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper fun main() = kotlinx.coroutines.runBlocking { val mapper = jacksonObjectMapper().setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE) val input = mapper.readValue("{\"kind\":\"uri\",\"uri\":\"https://example.com\"}", ExtractInput::class.java) val config = mapper.readValue("{\"url\":{\"crawl\":{\"document_url_depth\":1,\"follow_document_urls\":true,\"respect_robots_txt\":false,\"ssrf\":{}},\"mode\":\"document\"}}", ExtractionConfig::class.java) val result = Xberg.extract(input, config) println(result.results) println(result.results.get(1).content) }逐段拆解
ExtractInput(输入):{"kind":"uri","uri":"https://example.com"}表示本次输入是一个 HTTP(S) URI。在 Kotlin 绑定中它对应 ExtractInput.kt 的ExtractInput(kind = ExtractInputKind.URI, uri = ...),与本地路径、file://URI 一样共用Xberg.extract入口。ExtractionConfig(配置):核心是url段——mode = "document"告诉引擎把种子 URL 当作一个远程文档/页面来抓取,而不是站点级crawl;crawl段内打开递归文档链接的两个开关(见下节)。Xberg.extract(input, config):同步(阻塞)调用,返回ExtractionResult。由于递归开启,results不再是单元素,results.get(1)取到的是递归抓取到的第二个文档。- Jackson 注意点:Kotlin 绑定通过 JNI 与 Rust 核心通信,使用 snake_case 命名策略保证 JSON 字段与 Rust serde 的 wire 格式一致;
runBlocking说明该绑定是协程友好的。
夹具背后的断言(怎么验证"它真的递归了")
同一 fixture 在 url_recursive_document_urls.json 中给出了服务端 mock 与断言:
- mock 服务返回一个 HTML 首页,内含链接
linked.txt:/→<html><body><h1>Recursive source</h1><a href="/linked.txt">Linked document</a></body></html>/linked.txt→Recursive document target reached by Xberg.\n
- 断言
count_min(results) >= 2:只有递归真正发生了,才会产生第 2 个 result; - 断言
results[1].content包含Recursive document target:验证第二个结果确实是顺着链接抓到的文档内容。
对应地,端到端测试 UrlTest.kt 中的testUrlRecursiveDocumentUrls()把上面的 JSON 原样搬进 Kotlin 测试,并在本地起 mock server 后运行同样的断言。这套"fixture → 生成代码 → e2e 测试"的链路,正是仓库中验证递归文档提取行为的方式。
递归文档链接的配置项详解
递归行为由CrawlConfig上的两个字段控制,在 Kotlin 绑定中位于 CrawlConfig.kt,与它们配合的还有UrlExtractionConfig的若干边界参数(见 UrlExtractionConfig.kt)。
follow_document_urls:总开关(默认false)
/** Re-enqueue discovered LinkType.Document URLs into the crawl frontier so * the crawl follows links *from* document pages (PDFs, etc.) as it would * from HTML pages. Default: false (documents terminate at materialisation). */ val followDocumentUrls: Boolean = false默认关闭时,文档在"物化"(下载并提取)后就终结了——不会继续跟随文档内部发现的链接。置为true后,提取结果里发现的文档链接会被重新入队(re-enqueue)到抓取前沿,文档页和 HTML 页一样可以产生后续跳转。
document_url_depth:文档链深度(默认继承max_depth)
/** Maximum document-depth (from the seed URL through document links only) * when follow_document_urls is true. null means inherit max_depth. * Independent of max_depth: a document URL is enqueued only if BOTH the * outer max_depth and (if set) document_url_depth permit it. */ val documentUrlDepth: Int? = null注意两点关键语义:
- 独立于
max_depth:max_depth是"页面链接跳数",document_url_depth是"仅沿文档链接走的跳数",二者是**与(AND)**关系——一个文档 URL 只有在外层max_depth和(若设置的)document_url_depth同时放行时才会被入队; null时继承max_depth的值;夹具里显式设为1,即只沿文档链接追一层。
递归边界与安全兜底
递归不是无界的,UrlExtractionConfig提供了两道上限(默认值见 types.rs 中UrlExtractionConfig::default):
| 参数 | 默认值 | 作用 |
|---|---|---|
document_url_pattern | None | 正则过滤器,只允许匹配到的文档 URL 被跟随 |
max_document_urls_per_result | 100 | 单个提取结果最多跟随的 URL 数 |
max_total_urls | 1000 | 整个提取调用累计跟随的 URL 总数上限 |
此外,种子域名约束stay_on_domain/allow_subdomains对文档链接的默认行为是:页面链接始终限定在种子域名(及其子域)内,文档链接却默认允许跨主机——因为文档常托管在 CDN 或对象存储上;只有当你显式把stay_on_domain置为true时,才要求文档也必须落在种子域名内(见 CrawlConfig.kt 的注释说明)。
示例中的respect_robots_txt与ssrf
夹具配置里还有两项:
respect_robots_txt: false:测试场景为快速抓取 mock 服务,临时关闭 robots.txt 约束。生产环境抓取不受信任的站点时,官方指南建议保持respect_robots_txt与stay_on_domain开启,并显式设置max_pages/max_total_urls上限;ssrf: {}:空对象即使用默认 SSRF 策略——拒绝私有/内网地址、仅允许 http/https、最多 5 次重定向。Kotlin 端对应SsrfPolicy(见 SsrfPolicy.kt),默认deny_private = true。抓取用户提供的 URL 属于 SSRF 攻击面,务必保留该策略。
底层实现:递归链路在 Rust 核心中的样子
递归文档链接的"发动机"在 extract_impl.rs 的follow_recursive_document_urls(L1670-L1717)与enqueue_discovered_urls(L1719-L1744)两个函数中,逻辑清晰可读:
follow_recursive_document_urls ├─ follow_document_urls(config)? // 总开关关闭则直接返回 ├─ document_url_depth(config) == 0? // 深度为 0 同样直接返回 ├─ 编译 document_url_pattern 正则(非法正则 → validation 错误) ├─ enqueue_discovered_urls(初始结果, depth=1) // 首次入队 └─ while 队列非空: ├─ 以 ExtractInput::from_uri(uri) 递归调用 extract_one() ├─ depth < max_depth 时再次 enqueue_discovered_urls(depth+1) ├─ 累加 remote_urls / pages_crawled / documents_downloaded └─ results / errors 追加进总输出几个值得注意的实现细节:
- URL 从哪里来(
urls_from_result):每个ExtractedDocument结果中,uris里Hyperlink/Reference/Citation三类 URI 会被收集;同时正文content里出现的https?://文本也会被正则扫描出来(末尾标点会被 trim)。也就是说,无论是结构化的链接元数据还是纯文本里的 URL,都能进入递归队列。 - 跟随条件(
should_follow_discovered_url):必须是http/https协议;若有document_url_pattern则必须匹配;若stay_on_domain开启则主机必须属于种子域名或(allow_subdomains时)其子域。 - 去重与上限:
seen集合去重,命中max_total_urls(默认 1000)即停止入队;每个结果最多取max_document_urls_per_result(默认 100)个链接。 - 深度语义:
document_url_depth(config)在未显式设置时回退到crawl.max_depth,再回退到默认 1(见 L1802-L1817)。 - 结果顺序:递归抓到的文档按遍历顺序追加在初始结果之后,这正解释了示例中
results.get(1)能拿到第二个文档。
这段实现同时服务于extract的多种路径(L177/L385/L514 三处调用点),即无论走哪种 URL 摄取分支,递归逻辑都是同一份。
在 Android 工程中落地
把示例接入真实 Android 工程,推荐做法是直接使用 Kotlin 数据类构造而非字符串反序列化(仓库 packages/kotlin-android/README.md 的 Quick Start 即采用这种风格):
import io.xberg.* fun extractRecursively(url: String): ExtractionResult { val config = ExtractionConfig( url = UrlExtractionConfig( mode = UrlExtractionMode.DOCUMENT, crawl = CrawlConfig( followDocumentUrls = true, documentUrlDepth = 1, // 仅沿文档链接追一层 respectRobotsTxt = true, // 生产环境建议开启 maxPages = 50, maxConcurrent = 5, ssrf = SsrfPolicy(), // 默认拒绝内网地址 ), maxDocumentUrlsPerResult = 100, maxTotalUrls = 1000, ), ) return Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = url), config, ) } // 使用:results[0] 是种子页面/文档,后续元素是递归发现的文档 val output = extractRecursively("https://example.com") output.results.forEachIndexed { index, doc -> println("[$index] ${doc.content.take(200)}") }要点提示:
- JNI 加载:e2e 测试套件在
companion object中通过System.loadLibrary("xberg_jni")加载本地库,并默认开启CRAWLBERG_ALLOW_PRIVATE_NETWORK环境变量以允许测试访问本地 mock server(见 UrlTest.kt)。生产环境请勿放开内网访问限制; - 依赖版本:Kotlin DSL 中声明
implementation("io.xberg:xberg-android:1.3.6")即可(见 README.md); - 特性前提:递归文档链接依赖
url-ingestionCargo 特性(full构建已包含);WASM 目标不提供 URL 摄取能力,Android/JVM 不受影响; - 结果语义:
ExtractionResult信封里results为发现顺序的文档列表,errors记录单个输入的非致命错误,summary汇总remote_urls/pages_crawled/documents_downloaded等计数(见 types.rs),可用于在 UI 上展示抓取概况。
总结
Xberg 的递归文档 URL 提取,让你用一次Xberg.extract调用即可"页面 + 文档链"一网打尽。打开CrawlConfig.follow_document_urls、用document_url_depth设定文档链深度,再用max_document_urls_per_result/max_total_urls/document_url_pattern划定边界,即可在 Android 上获得可控、安全、可观测的递归提取能力。仓库内的 fixture(url_recursive_document_urls.json)、自动生成的 Kotlin 示例(url_recursive_document_urls.md)与端到端测试(UrlTest.kt)三份材料互相印证,是深入理解与二次开发这条特性的最佳入口。
- 后端
- 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.
相关推荐
使用 C 在 Xberg 中实现递归 URL 文档提取:从页面链接一路追到文件内容
使用 C 在 Xberg 中实现递归 URL 文档提取:从页面链接一路追到文件内容 Xberg 是一个以 Rust 为核心的 Polyglot 文档智能引擎,其
后端AI 应用NLPxberg 递归 URL 文档提取实战:用 follow_document_urls 沿文档链接自动抓取关联资源
xberg 递归 URL 文档提取实战:用 follow_document_urls 沿文档链接自动抓取关联资源 本篇技术指南聚焦 xberg 的 URL 递归
后端AI 应用NLPxberg Dart 实战:用 URL 递归提取配置自动跟随文档链接(fixture 全解析)
xberg Dart 实战:用 URL 递归提取配置自动跟随文档链接(fixture 全解析) 本篇指南围绕 xberg 仓库中 Dart 语言的 url_re
后端AI 应用NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考