面向中国科学技术大学公开站点的本地归档与检索工具。它遵守 robots.txt,不使用账号凭据、不绕过登录或访问限制,并将抓取结果保存在本地 SQLite 和内容寻址文件中。
- 从学校主页、新闻网、职能部门和院系站点发现公开页面。
- 可断点续跑,并支持只刷新新闻列表和当前文章的增量更新。
- 解析标题、作者、发布时间、栏目、摘要、正文、图片和附件。
- 保存原始响应、结构化文章 bundle、图片源地址、媒体、附件和完整审计状态。
- 提供只读网页预览和 JSON API,区分新闻与通知。
- 对每个配置来源执行离线抽样,验证来源归属、原始文件、字段、bundle 和媒体。
config/ 来源配置
src/ustc_crawler/ 抓取、解析、存储、路由和网页预览
scripts/ 质量报告、在线覆盖检查和离线抽样验证
tests/ 单元与集成测试
data/ 本地运行数据,不提交 Git
模块职责和数据流见 ARCHITECTURE.md。
需要 Python 3.13 和 uv。
uv sync --locked
uv run ustc-crawler discover-ustc
# 小规模试跑
uv run ustc-crawler crawl \
--config config/sources.yaml \
--units data/discovered_units.json \
--include-units --include-supplemental \
--max-pages 200 --max-pages-per-source 20首次完整抓取:
uv run ustc-crawler crawl \
--config config/sources.yaml \
--units data/discovered_units.json \
--include-units --include-supplemental \
--max-pages 0 --max-depth 6169 个来源的增量更新:
uv run ustc-crawler crawl \
--config config/sources.yaml \
--units data/discovered_units.json \
--include-units --include-supplemental \
--incremental --max-depth 6 \
--concurrency 12 --delay 0.5--incremental 只刷新 seed、新闻列表/feed 和需要修复的文章页,并跳过已归档的空壳、重复页、导航页和课程资源。请求按主机限速;中断后重新执行同一命令即可续跑。
# 数据统计与导出
uv run ustc-crawler stats
uv run ustc-crawler export
uv run ustc-crawler report
# 每来源离线抽样验证
uv run python scripts/validate_source_samples.py
# 只读本地预览
uv run ustc-crawler serve预览默认地址为 http://127.0.0.1:8765/。主要 API:
/api/summary/api/news?type=news|notice/api/sources/api/article?url=...
改进 Markdown 图片规则后可以直接从已保存的文章 body_html 修复数据。正文 HTML
仍作为源归档保存,修复会从它生成使用 /api/publications/images/{sha256} 地址的 Markdown。
sha256 是图片绝对源地址 UTF-8 字节的 SHA-256。大归档可以用 URL 游标和条数分块,
每次完成一个分块后只为实际处理的 article 写入同步 outbox:
uv run ustc-crawler rebuild-markdown \
--source unit-math-ustc-edu-cn \
--limit 1000
# 将输出的 last_url 设为 LAST_ARTICLE_URL 后继续下一页:
uv run ustc-crawler rebuild-markdown \
--source unit-math-ustc-edu-cn \
--after-url "$LAST_ARTICLE_URL" \
--limit 1000rebuild-markdown --source 只处理所选来源的已保存文章;--limit 0 表示全部文章,
--after-url 是按 article URL 排序的独占游标。此命令只更新 body_markdown 和图片
源元数据,原始 HTML 与其它文章字段保持不变,并按实际 article URL 写入 outbox。
重复执行会复用已有 revision 事件。需要同时应用完整解析规则(例如分类或正文抽取)时,
再使用 reindex;它同样会按实际重建的 canonical article 直接入队,不使用页面游标
作为同步游标。只需要同步已经用新规则抓取的文章,或重试此前只完成重建而未入队的记录
时,可以运行 sync-backfill;它使用按文章 URL 排序的独立 --after-url 和 --limit:
uv run ustc-crawler sync-backfill \
--source unit-math-ustc-edu-cn \
--chunk-size 100同步客户端使用部署在爬虫机器上的服务密钥,不依赖任何个人账号或本地密钥环。服务器地址和服务密钥分别通过环境变量提供;密钥只在内存中用于发送 X-Publication-Ingestion-Secret 请求头,不会写入 SQLite、归档、命令参数、输出或日志:
export USTC_CRAWLER_SERVER=https://example.invalid
export USTC_CRAWLER_INGESTION_SECRET='set-this-in-the-machine-secret-store'
# 先把历史文章分块写入本地 outbox,不发起网络请求
uv run ustc-crawler sync-backfill \
--db data/crawler.sqlite --data-dir data --chunk-size 100
# 重试并上传已持久化的不可变批次
uv run ustc-crawler sync \
--db data/crawler.sqlite --data-dir data \
--object-concurrency 8 \
--batch-concurrency 1批次默认最多 50 篇、单次请求上限 100 篇且正文约 2 MiB,断点重跑使用同一批次和幂等键;上传对象先从本地内容寻址 spool 校验 SHA-256/大小,再按服务端返回的请求头上传。未配置服务密钥时命令会以安全错误码退出。
同一批次内的对象上传和完成确认默认使用最多 8 个受限工作线程,并按计划顺序验证结果;可通过 --object-concurrency 或 SyncOptions.object_concurrency 调整到 1 至 64。批次默认串行投递;可通过 --batch-concurrency 或 SyncOptions.batch_concurrency 调整到 1 至 16。批次始终由协调线程串行认领并持久化,只有已认领的不可变批次并行投递;已保存的重试批次全部完成后才会认领新批次。--max-batches 仍按本次运行认领的批次数精确限制。
GitHub Actions 的 Incremental sync 每 6 小时运行一次。同步退出码 0 表示本轮
完成,2 表示仍有待重试批次、未发送事件或本轮事件校验失败;受控时间预算耗尽时
workflow 记录 124。后两种情况会先等待写入停止,执行 WAL checkpoint 和数据库
完整性检查,再保存缓存,最后将 workflow 标为失败。命令崩溃、强制终止、取消任务或
数据库检查失败时不覆盖上一份缓存。任务摘要和诊断附件列出退出码、队列计数,以及
未能组成批次的失败事件;历史失败事件的计数本身不会让后续已完成的同步继续失败。
抓取的逐来源错误会显示警告,并继续同步已归档内容。
旧事件不符合当前必填字段(例如 imageSources)时,重新入队失败批次无法重建其
内容。需要从归档文章生成当前格式事件,可手动运行:
gh workflow run incremental-sync.yml -f rebuild_markdown=true普通任务预算为 60 分钟,修复任务为 300 分钟,为大数据库的检查、压缩和缓存上传
预留时间。每轮最多选择 reindex、retext、rebuild_markdown 中的一种;
requeue_failed 可与它们组合。reindex 和 retext 最多运行 90 分钟,其后历史
入队最多 10 分钟;这两种命令超时不视为安全完成,因此不保存本轮缓存。
rebuild_markdown 默认处理全部已保存文章,重建和同步分别最多运行 120 和 70 分钟。
大归档可用
rebuild_limit 限制本轮文章数,并用日志中的最后完成游标作为下一轮
rebuild_after_url。例如每轮处理 10,000 篇:
gh workflow run incremental-sync.yml \
-f rebuild_markdown=true -f rebuild_limit=10000 \
-f rebuild_after_url="$LAST_ARTICLE_URL"重建的文章进入正常同步队列;部分完成或受控超时已保存的进度会留在新缓存中。 重建超时后应带游标继续剩余文章;普通同步重跑会继续处理已经入队的事件。
data/ 完全排除在 Git 之外。主要内容包括:
crawler.sqlite:抓取状态、页面、文章、媒体和附件关系。pages/:按响应 SHA-256 保存的原始内容。articles/:按文章 URL 哈希保存的 JSON 和正文 HTML。media/、assets/:去重后的图片和公开附件。exports/:JSONL、来源报告和验证报告。discovered_units.json:从官方院系目录生成的来源候选。
仓库只包含代码和静态来源配置。数据库、网页内容、备份、日志及生成报告不会被提交。
uv run ruff check src tests scripts
uv run pytest -qGitHub Actions 使用锁定依赖执行相同检查。
- 只跟随配置允许域名中的 HTTP(S) 链接。
- 遵守
robots.txt,不提交登录凭据。 - 登录门槛、403、验证码和不可访问页面只记录状态,不尝试绕过。
- PDF、Office 等公开文件作为附件归档,不冒充 HTML 文章。