ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ArchiveBox `archivebox add` 命令源码解析:URL 导入、Crawl 队列与递归抓取全流程

ArchiveBox `archivebox add` 命令源码解析:URL 导入、Crawl 队列与递归抓取全流程 ArchiveBoxarchivebox add命令源码解析URL 导入、Crawl 队列与递归抓取全流程【免费下载链接】ArchiveBox Open source self-hosted web archiving. Takes URLs/browser history/bookmarks/Pocket/Pinboard/etc., saves HTML, JS, PDFs, media, and more...项目地址: https://gitcode.com/gh_mirrors/ar/ArchiveBoxarchivebox add是 ArchiveBox 最核心的入口命令负责把单个 URL、批量 URL 或浏览器书签/Pocket/RSS/JSON 等导入文件转化为一次可执行的 Crawl爬取任务再由 Crawl Runner 生成 Snapshot 并调用各提取插件完成归档。本文以仓库中的 API 参考文档 archivebox.cli.archivebox_add.md 为骨架结合其实现源码 archivebox_add.py、输入解析工具 jsonl.py、URL/文件大小校验工具 util.py、Crawl 数据模型 models.py 以及完整测试套件 test_cli_add.py逐层拆解该命令的参数语义、内部调用链与执行模式。读完本文你将能精准掌握archivebox add的全部命令行参数、三种运行模式前台/后台/仅索引的取舍以及其输入安全防线与导入格式支持范围。模块概览archivebox.cli.archivebox_add该模块位于archivebox/cli/archivebox_add.py是整个 ArchiveBox 命令行体系的一个子命令模块。其 API 参考页暴露了以下公开符号符号类型作用__command__模块常量值为字符串archivebox add标识当前子命令名称_collect_input_urls(args, *, parserauto) - list[str]模块级函数从 CLI 位置参数或 stdin 收集并校验 URL 列表add(...) - tuple[Crawl, QuerySet[Snapshot]]核心业务函数创建 Crawl 并视模式而定启动 Runner 处理队列main(**kwargs)Click 命令入口声明全部 CLI 选项、解析参数并调用add()模块顶部的__command__ archivebox add常量被 CLI 框架用于命令名称登记add函数带有enforce_types装饰器会在运行时根据函数签名类型注解校验入参类型见 util.py 中enforce_types的实现它相当于一个精简版的 pydanticvalidate_call。核心函数add()参数语义与内部流程add()是archivebox add的实际执行体签名如下含全部默认值def add( urls: str | list[str], snapshot_ids: list[str] | None None, depth: int | str 0, max_urls: int 0, crawl_max_size: int | str 0, crawl_timeout: int 0, snapshot_max_size: int | str 0, crawl_max_concurrent_snapshots: int | None None, tag: str , url_allowlist: str , url_denylist: str , parser: str auto, plugins: str , persona: str Default, index_only: bool False, bg: bool False, created_by_id: int | None None, config: dict[str, Any] | None None, ) - tuple[Crawl, QuerySet[Snapshot]]参数校验与规格换算进入add()后首先做类型归一化与取值范围校验archivebox_add.pydepth强制转 int且只允许 0–4否则抛出ValueError(Depth must be 0-4)——对应 Crawl 模型上max_depth字段的MinValueValidator(0) / MaxValueValidator(4)约束见 models.pymax_urls、crawl_timeout必须 00 表示无限制crawl_max_size、snapshot_max_size通过parse_filesize_to_bytes()把人类可读字符串换算成字节数crawl_max_concurrent_snapshots缺省时继承运行时配置CRAWL_MAX_CONCURRENT_SNAPSHOTS且必须 1。parse_filesize_to_bytes位于 util.py支持45mb、2 GB、1gb这类带单位字符串大小写不敏感、允许单位与数字间有空格也接受纯数字字符串和 int/float遇到未知单位或不合法格式会抛ValueError最终由 Click 层转换为click.BadParameter输出友好的报错。冻结 Crawl 配置add()会把命令行选项映射为一组“冻结”进 Crawl 的配置键archivebox_add.pycrawl_config { PERMISSIONS: str(effective_persona_config.PERMISSIONS), **({INDEX_ONLY: True} if index_only else {}), **({PLUGINS: plugins} if plugins else {}), **({CRAWL_MAX_URLS: max_urls} if max_urls else {}), **({CRAWL_MAX_SIZE: crawl_max_size} if crawl_max_size else {}), **({CRAWL_TIMEOUT: crawl_timeout} if crawl_timeout else {}), **({SNAPSHOT_MAX_SIZE: snapshot_max_size} if snapshot_max_size else {}), **({PARSER: parser} if parser ! auto else {}), **({URL_ALLOWLIST: url_allowlist} if url_allowlist else {}), **({URL_DENYLIST: url_denylist} if url_denylist else {}), } crawl_config.update(config_overrides) # 调用方传入的 config 覆盖优先级最高源码注释明确指出调用方传入的config例如 API 层传入{ONLY_NEW: False}对“冻结型”配置键优先级最高而运行期派生的执行键会在Crawl.save()时被剥离等 Runner 实际执行 Hook 时再重新派生——这是 ArchiveBox 保证“一次提交、执行期环境一致”的关键设计。创建 Crawl 记录与 Runner 所有权随后add()创建一条Crawl记录archivebox_add.py关键字段包括urls保存用户提交的原始输入文本逐行 URL 或导入文件原文。源码注释特别强调这里刻意“逐字节”保留用户输入是为了让 API/UI/CLI 调用方可以审计或恢复导入过程不丢失 RSS/Netscape/JSON 中无法用“每行一个 URL”表达的元数据max_depth、tags_str、persona_id、label形如userhost $ archivebox add ... [时间戳]、created_by_idstatusQUEUEDretry_at在index_only时置为 None否则为当前时间表示可立即被 Runner 领取configcrawl_config。一个重要细节前台 add 必须先声明 Runner 所有权再发布可运行任务archivebox_add.py。源码注释解释了原因——如果在Crawl.objects.create()与current_command()之间的间隙让已有的 Server Runner 领走新 Crawl新增的 add 进程可能在其第一个 Snapshot Hook 执行到一半时把它杀死。因此add()在创建 Crawl 前会通过current_command(Process.TypeChoices.ADD, ...)注册当前进程身份。Crawl 记录创建成功后若传入的 URL 列表非空还会把第一个 URL 写回该Process记录便于追踪。三种执行模式add()根据index_only与bg标志分流archivebox_add.py--index-only仅索引打印黄色提示Index-only mode - URLs queued, runner not started直接返回(crawl, crawl.snapshot_set.none())即只把 URL 记入索引、不启动任何抓取进程。适合先登记、后由archivebox run批量处理的场景--bg后台打印提示后调用ensure_background_runner()确保存在后台 Runner随即返回。URL 进入队列由archivebox server或archivebox run --daemon启动的后台 Runner 择机领取前台模式默认进入一个while True循环通过standby_until_foreground_runner_needed()判断当前进程是否需要接管前台 Runner然后调用run_runner_worker([--crawl-id, str(crawl.id)], ...)执行爬取直到crawl.status变为SEALED或退出码表明工作完成。源码注释清晰描述了 Runner 的职责链- 处理 Crawl - 由所有 URL 创建 Snapshot - 处理 Snapshot - 运行提取插件extractors - 解析类插件发现新 URL - 创建子级 Snapshot - 重复直到达到 max_depth前台循环还处理了KeyboardInterrupt退出码 130与“共享 supervisord 被其他前台进程关闭”的竞态——当检测到exit_code 1且 supervisord 进程消失、而 Crawl 仍处于RUNNABLE_STATESQUEUED/STARTED见 models.py时会重新进入所有权循环继续完成任务并在中断时打印恢复命令archivebox run --crawl-idid的提示。任务收尾时会输出一段结构化摘要crawl 输出目录相对DATA_DIR的路径、Django admin 修改链接、total urls snapshotted、total size可读文件大小与total time。该摘要为 best-effort失败不会导致命令报错。CLI 入口main()全部命令行参数详解main()使用rich_click声明了完整的命令行界面并把add.__doc__Add a URL list or imported URL document to a new Crawl.注入为命令帮助文本。以下参数均可直接复制使用参数简写默认值含义--depth-d0递归归档链接页面的跳数仅允许0-4--max-urls—0本次 Crawl 最多快照的 URL 数0 不限--crawl-max-size—0Crawl 总大小上限支持45mb/1gb等单位0 不限--crawl-timeout—0Crawl 总运行时长上限秒0 不限--snapshot-max-size—0单个 Snapshot 大小上限0 不限--crawl-max-concurrent-snapshots—继承配置单个 Crawl 内并发的 Snapshot 数必须 ≥ 1--tag-t空为每个 Snapshot 添加的逗号分隔标签如tag1,tag2,tag3--url-allowlist/--domain-allowlist—空本次 Crawl 的 URL/域名白名单逗号分隔--url-denylist/--domain-denylist—空本次 Crawl 的 URL/域名黑名单逗号分隔--parser—auto输入 URL 的解析器auto, txt, html, rss, json, jsonl, netscape, ...--plugins-p空要运行的插件列表如title,favicon,screenshot,singlefile,...--extract—空要提取的插件或输出类型如title,pdf,text/html,image,...--persona—Default归档时使用的认证 Profile登录态--only-new/--no-only-new—继承ONLY_NEW跳过已有 Snapshot 的 URL--no-only-new强制重新归档--index-only—关闭只加入索引不立即归档--overwrite—关闭重新归档已存在的 URL等价于--no-only-new--update—关闭同上--no-only-new的别名--bg—关闭后台运行入队后立即返回urls位置参数—必填可传多个 URL也可通过 stdin 管道输入位置参数urls声明为nargs-1, typeclick.Path()可同时传入多个 URL。若没有位置参数且 stdin 不是 TTY则从 stdin 读取全部内容作为输入两者皆无时抛出click.UsageError(No URLs provided. Pass URLs as arguments or via stdin.)。--extract的插件解析--extract不是一个独立配置键而是main()在调用add()之前通过插件目录解析成--plugins的扩充archivebox_add.pycatalog get_plugin_catalog() tokens [token.strip() for token in extract.split(,) if token.strip()] plugin_names {name.lower(): name for name in catalog} selected [plugin_names[token.lower()] for token in tokens if token.lower() in plugin_names] selected catalog.matching_output(tokens)即先按插件名精确匹配再用matching_output(tokens)把输出类型如pdf、text/html、image匹配到对应插件两者均无匹配时抛出UsageError(fNo plugins found matching extract types: {extract})否则去重合并进--plugins。--only-new/--overwrite/--update的归一化--overwrite与--update是--no-only-new的语义别名三者统一归一化为config{ONLY_NEW: bool}传入add()archivebox_add.py最终成为 Crawl 冻结配置中优先级最高的覆盖项。URL 收集与输入安全_collect_input_urls()def _collect_input_urls(args: tuple[str, ...], *, parser: str auto) - list[str]该函数调用 jsonl.py 的read_args_or_stdin()有位置参数时逐个解析否则读取 stdin。每一行输入会经过parse_line()jsonl.py按以下优先级识别以{开头的行尝试按 JSON 解析作为带类型的 JSONL 记录支持type字段Crawl、Snapshot、ArchiveResult、Tag、BinaryRequest、Binary、Process、Machine无type但有url的记录自动补type: Snapshot以http://或https://开头的行识别为纯 URL补type: Snapshot36 位带连字符的 UUID 或 32 位十六进制串识别为 Snapshot ID空行、#注释行及其他无法识别的行被跳过。_collect_input_urls()在解析后做两件事对每条记录的url字段调用validate_url()util.py要求协议必须是http/https且包含主机名、必须为单行、长度不得超过MAX_URL_LENGTH否则抛ValueError并由外层转为click.BadParameter对记录中的urls多行字段逐行拆解、去空白、跳过#注释后同样校验。安全防线拒绝本地路径与注入载荷add()对传入 URL 的另一处校验是source_text的构造——所有 URL 同样经由validate_url()过滤。仓库测试 test_cli_add.py 的malicious_add_inputs()与test_add_rejects_file_path_and_shell_injection_payloads系统性验证了以下攻击向量全部被拒绝本地文件路径与file://URL/etc/hosts、../../../../etc/passwd、file:///etc/hostsShell 注入载荷; touch canary; #、 touch canary echo 、$(touch canary)、\touch canary带 XXE 的恶意 RSS!ENTITY localfile SYSTEM file:///etc/hosts与xi:include hreffile:///etc/hosts/。测试断言这些输入不会生成任何 Snapshot、不会在磁盘上留下 canary 文件。测试test_run_rejects_file_url_injected_directly_into_crawl_urls_with_db_update还验证了即使攻击者绕过 CLI 直接修改数据库写入file://URLRunner 领取 Crawl 时会再次校验并拒绝执行 Hook——安全校验发生在“入口”与“执行”两个环节。导入格式支持与元数据保真archivebox add的核心价值之一是“导入即归档”。测试test_add_stdin_import_formats_preserve_metadata_and_crawl_inner_urlstest_cli_add.py用一组真实 fixture 验证了以下导入格式格式说明保留的元数据txt纯文本行内 URL 会被正则提取URLrssRSS 2.0 XMLtitle、pubDate映射为bookmarked_at、category标签netscapeNetscape Bookmark HTML浏览器导出格式标题、ADD_DATE时间戳、TAGSdom普通 HTML 页面提取其中的a hrefURLjson单条 JSON 记录title、bookmarked_at、tagsjsonlJSON Lines 多记录同上逐行测试的关键断言包括每种格式各生成一条Crawl其urls字段与输入文件原文逐字节一致每条 Crawl 上tags_str cli-stdin-import最终由 Runner 处理出的 Snapshot 均携带正确的title、bookmarked_at与标签且不会产生archivebox://内部占位 URL。IMPORT_FORMAT_ENV表明这一能力依赖解析类插件parse_html_urls, parse_jsonl_urls, parse_netscape_urls, parse_rss_urls, parse_txt_urls的组合。与数据模型的对接Crawl / Snapshot / Process从数据模型视角看models.py每个add调用创建一个Crawl状态机初始为QUEUEDINITIAL_STATE经STARTEDACTIVE_STATE终态为SEALEDFINAL_STATESRUNNABLE_STATES (QUEUED, STARTED)决定 Runner 是否会领取它retry_at为 None 表示“暂停/仅索引”Runner 不会处理add()返回crawl.snapshot_set.all()即本次 Crawl 名下全部 Snapshot 的 QuerySet——这是 API/CLI 上层消费结果的统一出口前台 add 注册的ProcessProcess.TypeChoices.ADD承载命令身份command.mark_exited(exit_code)在函数结束含异常分支时记录退出码实现进程生命周期可审计。典型使用示例综合以上参数语义给出几个可直接运行的真实示例# 1. 前台归档单个 URL默认模式等待抓取完成并输出摘要 archivebox add https://example.com # 2. 同时归档多个 URL 并打标签 archivebox add --tagnews,tech --depth1 https://a.com https://b.com # 3. 递归两层、限制总大小 100MB、超时 10 分钟、并发 4 archivebox add --depth2 --crawl-max-size100mb --crawl-timeout600 \ --crawl-max-concurrent-snapshots4 https://example.com # 4. 从文件导入通过 stdin 管道不传位置参数 cat bookmarks.html | archivebox add --parsernetscape --bg # 5. 只登记索引不立即抓取稍后由 Runner 统一处理 archivebox add --index-only https://example.com archivebox run --crawl-idcrawl-id # 6. 强制重新归档已存在的 URL archivebox add --update https://example.com # 等价于 --overwrite / --no-only-new注意本地文件不能作为位置参数传入test_add_rejects_file_path_argument验证了这会导致No URLs provided报错必须通过 stdin 管道喂入内容——这是刻意设计的安全边界见 jsonl.py 的模块说明。小结archivebox add是 ArchiveBox 抓取流水线的总闸门它负责输入解析与安全校验、Crawl 配置冻结、任务入队与 Runner 调度最终产出可审计的(Crawl, QuerySet[Snapshot])。理解其add()/_collect_input_urls()/main()三层结构、三种执行模式的差异以及“入口 执行”双重复核的安全设计可以帮助你更可靠地批量导入历史书签、更精细地控制递归抓取资源消耗也能为上层 APIv1_api.py 等调用该命令时正确传参提供源码级依据。相关实现与验证可继续深入阅读 archivebox_add.py、jsonl.py、util.py、models.py 与 test_cli_add.py。【免费下载链接】ArchiveBox Open source self-hosted web archiving. Takes URLs/browser history/bookmarks/Pocket/Pinboard/etc., saves HTML, JS, PDFs, media, and more...项目地址: https://gitcode.com/gh_mirrors/ar/ArchiveBox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表