Skip to content
Life-USTCPublic

About

Respectful, resumable crawler and local archive for public USTC sites

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

USTC Public Site Crawler

面向中国科学技术大学公开站点的本地归档与检索工具。它遵守 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 6

169 个来源的增量更新:

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 1000

rebuild-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 -q

GitHub Actions 使用锁定依赖执行相同检查。

访问边界

  • 只跟随配置允许域名中的 HTTP(S) 链接。
  • 遵守 robots.txt,不提交登录凭据。
  • 登录门槛、403、验证码和不可访问页面只记录状态,不尝试绕过。
  • PDF、Office 等公开文件作为附件归档,不冒充 HTML 文章。

About

Respectful, resumable crawler and local archive for public USTC sites

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages