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>
377 lines
14 KiB
Markdown
377 lines
14 KiB
Markdown
# 立创开源硬件平台 (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 | 质量档位(观察到 0–4;4 = 精品徽章) |
|
||
| `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 JSON(Next.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 包含所有档位(0–4),不是仅精品子集。这就是"立创开源广场"的公开可下载全量。
|
||
|
||
> **注**:该数字**不等于**立创 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 ← 容量上界 |
|
||
|
||
**建议预算**:**150–180 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% NC(Non-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 分布)合并成本文 |
|