Files
FacereDataset/docs/sources/oshwhub.md
Zhang Jiahao ed4837dedf Rewrite oshwhub.md as canonical data source investigation
Why:
- Charles 要求把 12493 总数验证 + 90 项目采样结果合进主调研文档,消除
  oshwhub_corpus_estimate.md 与 oshwhub.md 的重复与分散。
- 一份高质量的数据源调查应该独立完备:任何人(人或 agent)读完就能
  复现爬取 / 估算 / 合规判断,不用跨文件拼凑。

What:
- docs/sources/oshwhub.md 重写为 9 节 + 附录:
  - TL;DR 表(一页纸核心事实)
  - 站点架构 / robots / API 入口 / 项目详情 SSR / 附件 CDN
  - 排除项:fs-web-stream.jlc.com 推广图标 / u.lceda.cn 登录源
  - §4 项目总数验证(新):三路 sort 一致 12493 + 分页二分边界 ≈250 页 + grade 覆盖抽样
  - §5 抽样语料特征(从 corpus_estimate 并入):体积 median 9MB/p90 54MB、
    视频占 54%、license 分布 GPL 3.0 49%/Public Domain 21%
  - 风险表 7 条、附录重跑命令
- 删除 docs/sources/oshwhub_corpus_estimate.md(内容已并入 §5)
- log.md: 本次记录

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 19:59:05 +08:00

377 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 立创开源硬件平台 (oshwhub.com) — 数据源调研
**平台**:立创开源硬件平台(嘉立创 EDA / 深圳创电优选科技有限公司)
**URL**https://oshwhub.com
**首版调研**2026-04-23
**维护**:每次爬虫新发现或数据结构变更须更新本文件
---
## TL;DR
| 指标 | 值 | 备注 |
|---|---|---|
| 公开项目总数 | **12 493** | 已发布到开源广场的项目(见 §4 验证) |
| 抓取复杂度 | 低 — 中 | 列表 API 开放,详情走 SSR HTML 解析 |
| License 主流 | GPL 3.0 (49%) / Public Domain (21%) / CC 系列 (20%) / CERN·TAPR OHL (6%) | 采样 90 项目,见 §5.3 |
| 单项目体积中位数 | **9 MB**mean 22 MB, p90 54 MB, max 204 MB | §5.1 |
| 全量存储估算 | **110 GB (median)** / 660 GB (p90 上界) | §5.1 推算 |
| 反爬风险 | 低 | 列表 API 无 rate-limit 实测,建议 QPS ≤ 0.5 自律 |
| 登录态要求 | 不需要 | 所有数据走匿名端点,见 §3 |
| 当前已入库 | 10 项目 / 52 附件 / 535 MB | 见仓库 `projects.md` |
**爬虫实现**`crawlers/oshwhub/crawler.py`
---
## 1. 站点架构
- **Next.js App Router SPA**。首页、列表页由客户端 fetch 渲染(首屏 HTML 无卡片 DOM详情页 `/<user>/<slug>``/detail/<uuid>`**SSR**,首屏 HTML 已包含完整元数据与附件列表
- axios baseURL = `https://oshwhub.com`(见 `/_next/static/chunks/8661-*.js`
- CDN
- 封面 / 附件:`https://image.lceda.cn`
- 嘉立创服务侧栏图标:`https://fs-web-stream.jlc.com`(与项目内容无关,见 §3.4
## 2. robots.txt 与合规
```
User-agent: *
Disallow: /posts
Sitemap: https://oshwhub.com/sitemap.xml
```
`/detail/``/explore``/api/project` 均**允许**抓取。本项目所有抓取严格按 robots 执行。
---
## 3. API 与抓取入口
### 3.1 项目列表 — 主入口
```
GET https://oshwhub.com/api/project?page=N&pageSize=M&sort=hot
```
| 参数 | 含义 | 备注 |
|---|---|---|
| `page` | 页码(从 1 起) | |
| `pageSize` | 页大小 | 最大 **50**;超过返回空 |
| `sort` | 排序 | `hot`(默认)/ `like` / `new` |
返回:
```jsonc
{
"success": true,
"code": 0,
"result": {
"page": 1,
"pageSize": 50,
"total": 12493,
"totalPage": 250,
"lists": [ /* project items */ ]
}
}
```
**每条 list item 字段**
| 字段 | 类型 | 说明 |
|---|---|---|
| `uuid` | string (32 hex) | 项目全局 ID |
| `name` | string | 项目名 |
| `introduction` | string | 简介 (≤ 200 字) |
| `path` | string | `<username>/<slug>`,用于拼详情 URL |
| `owner` | object | `{uuid, username, nickname, avatar, team}` |
| `count` | object | `{fork, star, like, views, watch}` |
| `grade` | int | 质量档位(观察到 044 = 精品徽章) |
| `thumb` | string | 封面(以 `//image.lceda.cn/``/images/` 开头) |
| `tags` | string[] | 可能为空 |
| `created_at` | ISO 8601 | 项目**创建**时间EDA 编辑器创建) |
| `updated_at` | ISO 8601 | 项目上次更新时间 |
| `oshwhub_publish_at` | ISO 8601 | **发布到开源广场**的时间(`sort=new` 按此排) |
| `public`, `publish` | bool | 本 API 返回的记录里恒为 true |
| `comments_count` | int | |
> **陷阱**`sort=new` 实际按 `oshwhub_publish_at` 排,**不是** `created_at`。`page=1` 下最早可能看到 `created_at: 2021-01-17` 的项目(因为它刚发布到广场)。
### 3.2 项目详情 — SSR HTML 解析
```
GET https://oshwhub.com/<path> # 例https://oshwhub.com/CYIIOT/ST_LINK-V2_1
GET https://oshwhub.com/detail/<uuid> # 等价
```
HTML 内嵌 escaped JSONNext.js RSC payload关键字段位置
```html
<title>自制ST-LINK V2-1开源版本 - 立创开源硬件平台 - 深圳创电优选科技有限公司</title>
<meta name="description" content="最近迷上了攻城狮工具的制作..." />
...
<!-- 正文 HTML 中某处(在 RSC 字符串里,用 \" 转义):-->
\"license\":\"GPL 3.0\"
\"creator\":{\"uuid\":\"367b6ee2c2114a459898e14b1268a641\"}
\"attachments\":[
{\"id\":1,\"uuid\":\"83ade303...\",\"name\":\"ST-Link V2.1官方图纸.pdf\",
\"src\":\"/attachments/2020/7/mRn5hQ....pdf\",
\"mime\":\"application/pdf\",\"ext\":\"pdf\",
\"size\":183323,\"md5\":\"bdb976690426a0e3216ad3aacd9878cc\",
\"download_count\":3720},
...
]
```
提取流程见 `crawlers/oshwhub/crawler.py::parse_detail_html`
1. `<title>` → 项目标题(去后缀)
2. `<meta name="description">` → 简介
3. `re.search(r'\\"license\\":\\"([^\\"]+)\\"')` → license 字段
4. 定位 `\\"attachments\\":[`,括号平衡法截出数组,反转义 `\"``"``json.loads`
### 3.3 附件下载 CDN
```
https://image.lceda.cn{attachments[].src}
# 例https://image.lceda.cn/attachments/2020/7/mRn5hQ....pdf
```
- HTTP 200**无鉴权**(直接 curl 即可)
- 覆盖类型PDF / ZIP / RAR / 7z / MP4 / CSV / BIN / JSON / JPG / PNG 等用户自传全类型
- **这是项目用户文件的唯一入口**,已穷举排查确认
### 3.4 已排查的「非项目源」路径
- `https://fs-web-stream.jlc.com/fs-web-stream/file-operation/download/<snowflake-id>` — 详情页 HTML 里会出现这类 URL十几个每页但 context 都是**嘉立创服务侧栏/推广图标**
- "嘉立创 3D 打印" icon
- "开源硬件平台" badge
- "EDA 扩展广场" banner
- "Ican" / "发热片" 等产品线图标
**与项目本身无关**,爬虫应跳过。
### 3.5 未开放 / 需登录的端点
| 端点 | 现象 | 说明 |
|---|---|---|
| `/api/project/<uuid>` | `{"code":104001,"success":false}` | GET / POST / 带参数均失败;疑似需 session作用不明 |
| `u.lceda.cn/api/projects` | HTTP 401 "尚未登录" | EasyEDA 编辑器的工程源(真正的 schematic / PCB JSON需登录态 |
| `u.lceda.cn/api/project` | 302 重定向 | 同上 |
| `/api/search/*` | 空响应 | 可能需要 CSRF token |
**当前策略**:通过 SSR HTML 解析拿到所有公开字段,工程源 JSON`u.lceda.cn`)留作 v0.2+ 评估是否值得引入登录态。
---
## 4. 项目总数验证
API 报告 `total: 12493`(三种 sort 下一致),采用两步交叉验证:
### 4.1 三路 sort 一致性
```bash
for sort in hot new like; do
curl -s "https://oshwhub.com/api/project?page=1&pageSize=1&sort=$sort" \
| python3 -c "import sys,json;print(json.load(sys.stdin)['result']['total'])"
done
# 三次输出均为 12493
```
### 4.2 分页边界二分搜索
`pageSize=50` 二分查找最后一页有数据的页号:
```
page 2000 -> 0 items page 253 -> 0
page 687 -> 0 page 251 -> 0
page 468 -> 0 last real ≈ 250
```
250 × 50 = **12 500**,与 `total=12493`(最后一页 43 条不满)完全吻合。
### 4.3 覆盖范围抽样
`sort=new` 翻到中间几页,`grade` 档位分布:
| 页 | 观察到的 grades |
|---|---|
| 1 | 全 grade=4精品 |
| 100 | grade=3 |
| 200 | grade=2 |
| 300 | grade=2 |
| 1000 | grade=0 |
**结论**12 493 包含所有档位04不是仅精品子集。这就是"立创开源广场"的公开可下载全量。
> **注**:该数字**不等于**立创 EDA 编辑器里所有用户工程的总数——私有草稿、未发布工程不在 API 范围内也不应被本项目爬取。12 493 是合规可抓的上限。
---
## 5. 抽样语料特征90 项目实测)
**重跑命令**`uv run python scripts/estimate_size.py --pages 3 --page-size 30 --sort hot`
**采样方法**:只解析详情 HTML 的 `attachments[].size` 求和,**不下载**附件
**采样日期**2026-04-23
### 5.1 单项目体积分布
| 指标 | 附件数 | 体积 |
|---|---|---|
| mean | 3.1 | 22.2 MB |
| median | 2 | **9.0 MB** |
| p90 | — | 54.2 MB |
| max | 15 | 204.5 MB |
90 个样本合计 2001 MB。**偏差说明**:样本来自 `sort=hot` top 90偏向有人气、附件齐的项目长尾低人气、无附件项目的中位数会更低。
**全量 12 493 项目存储推算**
| 基数 | 估算 |
|---|---|
| mean × total | 271 GB |
| median × total | **110 GB** ← 推荐规划值 |
| p90 × total | 662 GB ← 容量上界 |
**建议预算****150180 GB**median + 15% buffer+ 考虑 hot 偏差修正后向下取)。
### 5.2 文件类型分布(按字节)
| 后缀 | 样本总量 | 占比 |
|---|---|---|
| `.mp4` | 1 029 MB | **51%** |
| `.zip` | 676 MB | 34% |
| `.rar` | 72 MB | 4% |
| `.qt` | 66 MB | 3% |
| `.pdf` | 32 MB | 2% |
| `.bin` | 27 MB | 1% |
| `.jpeg` | 26 MB | 1% |
| `.7z` | 10 MB | <1% |
> **关键洞察**:视频(`.mp4 + .qt` ≈ 54%)占存储一半。训练语料如果主要用 PCB / 原理图 / BOM / 叙事,加 `--skip-ext mp4,qt,mov,avi` 可**直接省一半**存储。
### 5.3 License 分布90 项目样本)
| License | 计数 | 占比 |
|---|---|---|
| GPL 3.0 | 44 | **49%** |
| Public Domain | 19 | 21% |
| CC BY-NC-SA 4.0 | 5 | 6% |
| CERN Open Hardware License | 4 | 4% |
| CC BY-NC-SA 3.0 | 3 | 3% |
| CC BY-SA 4.0 | 2 | 2% |
| TAPR Open Hardware License | 2 | 2% |
| CC-BY-NC-SA 3.0 | 2 | 2% |
| 其他 CC 变种 | 2 | 2% |
**全部开源 / 公共领域**,样本中**无闭源**。但注意:
- **49% GPL 3.0**:训练模型无直接违反,但下游若发布模型权重 + 数据集作为 derivative work保守应沿用 GPL 或评估 scope
- **~11% NCNon-Commercial**:商用场景**必须过滤剔除**
- 全量里 `license: "unknown"`(作者未选)比例可能更高,下游按 whitelist 过滤;默认建议 whitelist
```
{Public Domain, CC0, CC BY 4.0, CC BY-SA 4.0, MIT, Apache-2.0, BSD*, CERN-OHL*}
```
---
## 6. Schema 映射
对齐 `schemas/project.schema.json`(跨源统一 schema
| 本项目 schema 字段 | oshwhub 来源 |
|---|---|
| `source` | 常量 `"oshwhub"` |
| `source_url` | `https://oshwhub.com/<path>` |
| `project_id` | `uuid`32 hex |
| `title` | 详情页 `<title>` 去后缀,或列表 `name` |
| `description_short` | 列表 `introduction` |
| `description_path` | `"description.md"`(本地文件) |
| `author.username` | `owner.username` |
| `author.display_name` | `owner.nickname` |
| `author.user_id` | `owner.uuid` |
| `license` | SSR HTML 嵌入 JSON 的 `license` 字段;缺省 `"unknown"` |
| `tags` | 列表 `tags` |
| `created_at`, `updated_at` | 列表同名字段 |
| `published_at` | `oshwhub_publish_at` |
| `crawled_at` | 运行时 `datetime.now(timezone.utc)` |
| `metrics.{likes,stars,forks,views,watch,comments}` | `count.{like,star,fork,views,watch}` + `comments_count` |
| `cover.url` / `cover.path` | `thumb` / 本地 `cover.*` |
| `files[].{name,url,size,md5,ext,mime,original_id}` | `attachments[]` 逐字段映射 |
| `files[].{path,sha256}` | 下载后写入 |
| `raw_fields.{path,grade,origin,public,publish}` | 保留源站原始字段 |
---
## 7. 速率与礼貌
当前配置(`crawlers/oshwhub/crawler.py`
| 动作 | 频率 |
|---|---|
| 列表 API | 页间隔 2 s按 `SLEEP_BETWEEN`QPS 0.5 |
| 详情 HTML | 同上,每次 ~500 KB |
| 附件下载 (`image.lceda.cn`) | 每个文件后 2 s sleep |
UA 声明为 `FacereDataset/0.1 (+https://git.deepknow.site/Facere/FacereDataset)`**不伪装浏览器**。
**实测无 rate-limit 触发**。放量时建议并发 ≤ 3遇 429/5xx 指数退避,连续失败 ≥ 5 次停爬人工介入。
---
## 8. 目录输出约定
每个项目落盘到:
```
data/raw/oshwhub/<uuid>/
├── metadata.json # 符合 schemas/project.schema.json
├── description.md # 标题 + 简介 + 作者/许可证/发布时间
├── cover.{jpg,png,jpeg} # 封面(普通 git
├── _urls.json # 所有原始 URL 清单(重下用)
└── files/ # 原始附件Git LFS见 .gitattributes
├── xxx.pdf
├── xxx.zip
└── ...
```
---
## 9. 已知风险与未解决
| # | 风险 / 问题 | 影响 | 当前策略 / 缓解 |
|---|---|---|---|
| R1 | 站点加反爬UA 指纹 / 验证码 / IP 限) | 放量中断 | 自律 QPS ≤ 0.5;若触发切换到 `lightpanda` 或真 Chrome + 降速 |
| R2 | `license: "unknown"` 或非标准字符串 | 下游合规风险 | 严格 whitelist 过滤;`oshwhub_license_normalize` 待建 |
| R3 | 单项目附件 > 100 MB | LFS 存储激增 | 目前 p90=54 MB 可接受;大于阈值记录 URL 不下载的开关待加 (`--max-file-size`) |
| R4 | EasyEDA 源 JSON 不可得 | 训练数据缺了"真正的源" | v0.2+ 评估登录爬的 ROI |
| R5 | 长描述 HTML 格式保真 | 文本语料噪声 | 当前只存简介;长文本提取(含图片/表格)留待 Phase 5 清洗 |
| R6 | `grade` 字段含义未官方公开 | 质量筛选判据模糊 | 经验grade=4 是精品徽章;其它档位抽样判据后续补 |
| R7 | 增量更新策略 | 重复抓取浪费 | `updated_at` 对比 + LFS prune 策略未实现 |
---
## 附录 A — 一次性重跑
```bash
# 规模估算(无下载)
uv run python scripts/estimate_size.py --pages 3 --page-size 30 --sort hot
# 列表 API 探测
curl -s "https://oshwhub.com/api/project?page=1&pageSize=1" | python3 -m json.tool
# 某项目详情
curl -s "https://oshwhub.com/CYIIOT/ST_LINK-V2_1" -o /tmp/detail.html
# MVP 爬取 10 个高质量
uv run python -m crawlers.oshwhub --top 10 --min-likes 50 --min-grade 4
# 指定 UUID 列表爬
uv run python -m crawlers.oshwhub --uuids aaa,bbb,ccc --out data/raw/oshwhub
```
## 附录 B — 变更历史
| 日期 | 变更 |
|---|---|
| 2026-04-23 | 首版API 探索、SSR 解析方案、fs-web-stream 排查、12 493 总数验证、90 项目采样(体积 / 类型 / license 分布)合并成本文 |