# 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 项概况 | 维度 | 值 | |---|---| | 总数 | 50(25 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 头部**(前 5):HelloWord-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 min,QPS=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//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 ""] += 1 print(licenses) EOF ``` 预期会有大量 NC-SA / GPL(参考已抓 5 Pro:5/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.x(chain replay) - ~3 个 Pro 2.x(plaintext 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,)" ``` 每项 ~30s(QPS=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 的限流真上限