crawler: --skip-ext + --max-source-mb gates for batch-50 expansion

Two CLI gates needed before scaling Pro batch beyond top-5:

--skip-ext mp4,qt,mov  (attachment filter)
  Skips video extensions in attachment download. Phase 1 measurements
  showed mp4+qt occupy ~54% of attachment storage. Entry still recorded
  in metadata.json with skipped:ext:<token> so we can re-fetch later if
  the policy changes. Honors both server-declared `ext` and filename
  suffix, case-insensitively.

--max-source-mb N  (Pro source size cap)
  Trips inside the chain replay loop on encrypted-blob total. On trip:
  raise ProjectOversizeError, wipe partial source/, append a row to
  data/state/oshwhub_pro_oversize.jsonl. Lets us shortlist 50+ Pro
  projects without one X86-board-class outlier (~500 MB) blowing the
  LFS budget. Std and Pro 2.x legacy are not capped (both <2 MB in
  sample).

Verified:
  - cap=0 trips on first blob (1.2 MB), source/ wiped, state recorded
  - cap=100 runs full ESP-VoCat (7.5 MB plain, 278 docs)
  - skip-ext microtest: 8/8 cases (case-insensitive, declared/suffix
    fallback, empty-token edge cases)

Plan + frozen candidate list for the next 50 projects:
  - docs/plans/oshwhub_batch50.md
  - data/state/oshwhub_batch50_candidates.jsonl (gitignore exception added)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 00:24:55 +08:00
parent e61404478e
commit eee1a9b97e
5 changed files with 347 additions and 5 deletions

View File

@@ -0,0 +1,196 @@
# oshwhub 扩抓批 50执行计划
**创建**2026-04-29
**目标**:在已落库的 15 项5 Pro + 10 Std基础上再抓 50 个高质量项目
**候选清单**`data/state/oshwhub_batch50_candidates.jsonl`50 行)
**承接**`docs/sources/oshwhub_listing_full.md`(全量索引盘点)
---
## 1. 目标
- 把 corpus 从 15 项扩到 65 项,覆盖 Pro + Std + Pro 3.x + Pro 2.x 各形态
- 在 LFS 1 TB 红线内,先做 ~3-4 GB 量级的中等规模验证
- 结束后用 license 白名单评估这 50 项里有多少能进 Forge 投影
- 暴露 ≥ top-5 才会出现的 corner case例如超大 Pro 项目、私有附件、跨账号 fork
**显式不做**详情页全量扫描19h 工作量,留待后续按 A 档全池一次性做);下游 KiCad 转换Phase 3 还在做)。
---
## 2. 候选池筛选标准
数据源:`data/state/oshwhub_listing_full.jsonl`33,695 项全量 listing 索引)
**硬过滤**
- `grade >= 3 AND like >= 10`A 档)
- `uuid` 不在已抓 15 项内
**排序**:复合质量打分
```
score = like*3 + star + fork*2 + views/100 + comments_count*2 + grade*50
```
**多样性约束**
- 单作者上限 2 个项目(避免 `course-examples` / `45coll` 等高产账号占满)
- Pro / Std 25:25 平衡Pro 是新平台增长焦点Std 是 7 年质量积累)
---
## 3. 选出的 50 项概况
| 维度 | 值 |
|---|---|
| 总数 | 5025 Pro + 25 Std|
| 唯一作者 | 43 |
| Max 单作者 | 2 |
| like 区间 | min=246 / median=554 / max=2137 |
| view 区间 | min=54k / median=117k / max=362k |
| grade 分布 | 4: 33 项, 3: 17 项 |
**Pro 头部**(前 5HelloWord-Keyboard 2137 likes / STM32 桌面宠物 / OV-Watch 智能手表 / mini 加热台 / ESP-SparkBot
**Std 头部**(前 5触摸调光雪花灯 1036 likes / 桌面可调电源 / 立创 EDA PCB 直尺 / 自平衡莱洛三角 / PN532 NFC
完整名单见 `data/state/oshwhub_batch50_candidates.jsonl`
---
## 4. 执行步骤
### Step 0 — 候选池冻结 ✅
`data/state/oshwhub_batch50_candidates.jsonl` 已生成,作为本次批的"事实档案"。后续就算重排序也以这份为准。
### Step 1 — 详情页抓 license~5 minQPS=0.5
只对这 50 项扫详情页,提取 license / 完整 attachments 列表 / 原始描述。
不在这一步下载附件、不下源工程,**只是获取每项的 metadata 全集**。
```bash
PYTHONUNBUFFERED=1 uv run python -u -m crawlers.oshwhub \
--uuids "$(jq -r .uuid data/state/oshwhub_batch50_candidates.jsonl | paste -sd,)" \
--no-files \
--out data/raw/oshwhub
```
> 目前 `crawl_one` 已经会落 metadata.json + description.md + cover`--no-files` 跳过附件。
**预期产出**50 个 `data/raw/oshwhub/<uuid>/metadata.json`,含 license 字段。
### Step 2 — license 分布盘点
写个一次性脚本读 50 份 metadata.json统计 license 分布。**这一步只做盘点不做过滤**——按 CLAUDE.md "研究用、不分发" 原则,所有 license 都入库。下游 Forge 投影时再用白名单过滤。
```bash
uv run python - <<'EOF'
import json, glob, collections
licenses = collections.Counter()
for p in glob.glob("data/raw/oshwhub/*/metadata.json"):
m = json.load(open(p))
if m["uuid"] in batch_50_uuids:
licenses[m.get("license") or "<none>"] += 1
print(licenses)
EOF
```
预期会有大量 NC-SA / GPL参考已抓 5 Pro5/5 都是 GPL/NC-SA
### Step 3 — Pro 子集预检 + 抓源(~30-60 min
25 个 Pro 项目里要先判 3.x vs 2.x`branch_uuid` 是否 null然后对应走 modern / legacy 路径:
```bash
PYTHONUNBUFFERED=1 uv run python -u -m crawlers.oshwhub \
--backfill-pro-source \
--uuids "$(jq -r 'select(.origin=="pro") | .uuid' \
data/state/oshwhub_batch50_candidates.jsonl | paste -sd,)" \
--pro-cookie ~/.secrets/pro-lceda-cookie-header.txt
```
**时间预算**(已应用 chain replay sleep 优化commit `1e06ba6`
- 小型项目chain ~10~30s 各
- 中型chain ~30-50~1-2 min 各
- 大型chain ~100+~3-5 min 各
- 超大X86 量级 chain ~700~3-5 min 各CDN 部分降到 0.2s/req
按已抓 5 项观察,预期 25 项里:
- ~22 个 Pro 3.xchain replay
- ~3 个 Pro 2.xplaintext dataStr
### Step 4 — Std 子集抓源(~30 min
```bash
PYTHONUNBUFFERED=1 uv run python -u -m crawlers.oshwhub \
--backfill-source \
--uuids "$(jq -r 'select(.origin=="std") | .uuid' \
data/state/oshwhub_batch50_candidates.jsonl | paste -sd,)"
```
每项 ~30sQPS=0.2 / 5s sleep × 几个 API 调用25 项 ≈ 12-15 min。
### Step 5 — 附件下载(按需,可选)
附件Gerber / STEP / PDF / 视频)默认存 LFS但视频占大头之前 Phase 1 实测 mp4+qt 占 54%)。
**建议加 `--skip-ext mp4,qt,mov`** 节省 30-50% 体积。
```bash
PYTHONUNBUFFERED=1 uv run python -u -m crawlers.oshwhub \
--uuids "$(jq -r .uuid data/state/oshwhub_batch50_candidates.jsonl | paste -sd,)" \
--out data/raw/oshwhub
# 注:当前 crawler 没有 --skip-ext 选项;要加得改 crawl_one如果偷懒就先全抓再人工删
```
> 这一步没那么紧迫,可以挪到 Step 3/4 完成后单独跑。
### Step 6 — 验收 + 记录
`scripts/build_index.py` 重建 `projects.md`,更新 `log.md`,把统计信息写到 `docs/plans/oshwhub_batch50.md` 末尾"实施结果"段。
---
## 5. 资源预算
| 项 | 估算 | 备注 |
|---|---:|---|
| API 调用 | ~600 次 | 50 详情 + 25×3 Pro meta + 25 Std API + chain blobs |
| 时间 | **~1.5-2 小时** | 远低于之前估算的 19h因为只 50 项 + sleep 优化)|
| 存储(源工程)| ~1.5 GB | Pro 平均 30 MB、Std 平均 15 MB |
| 存储(附件,估)| 2-3 GB | 含视频;不含视频估 1 GB |
| **总 LFS 增量** | **~3-4 GB** | 远低于现有 LFS 余量 |
| 网络下行 | ~4 GB | 大部分走 LFS |
距离 200 GB"对象存储迁移决策点"还很远。
---
## 6. 风险与预案
| 风险 | 概率 | 影响 | 预案 |
|---|---|---|---|
| Pro cookie 过期半路 | 中 | 卡住 Pro 抓取 | 重抓 cookie爬虫已有 `oshwhub_pro_failed.jsonl` 重试机制(**未实现**,现在失败只 stderr|
| 单个 Pro 项目超大X86 量级 ~500 MB | 中 | LFS 单项膨胀 | 设置 size cap超过 200 MB 跳过 + 记 state |
| license 全是 NC-SA → 下游 Forge 投影门槛低 | 高 | Forge 阶段才发现 | 提前在 Step 2 盘点;**本批不做过滤**,只入库 |
| 某个项目附件含 mp4 视频 → 附件总量爆炸 | 中 | 多消耗 1-2 GB LFS | 加 `--skip-ext mp4,qt,mov` 选项(需小改 crawler|
| 抓 50 项过程中触发反爬 / 限流 | 低 | 中途中断 | 已有 5s/req QPS如果触发加 jitter从中断点续 |
---
## 7. 验收标准
- [ ] `data/raw/oshwhub/` 下新增 ≥ 45 个项目目录(允许 ≤ 5 项失败)
- [ ] 每个新增项目的 `metadata.json``license``source_format``source_path`
- [ ] Pro 25 项里 3.x / 2.x 分类正确(看 `source_format` 字段)
- [ ] LFS 增量 ≤ 5 GB
- [ ] `projects.md` 自动重建后包含新 50 项
- [ ] log.md 顶部一条总结性日志
---
## 8. 后续
成功结束后的下一步候选:
1. **license 详情扫描全 A 档** —— 把 license 信息覆盖到 2,806 项 A 档候选池,为下一次扩抓提供过滤条件
2. **扩抓批 200**(按 license 白名单 + 同样多样性策略)
3. **Pro 工程二进制图IMAGE/)补抓** —— 当前的真实数据 gap要不要补取决于下游需求
4. **风控压测** —— 在某一批做并发提速试验,找 oshwhub / pro.lceda.cn 的限流真上限