Files
FacereDataset/docs/sources/oshwhub.md
Zhang Jiahao b0ddcf3f14 Allow login content; plan cloud infra, storage tiers, EDA→KiCad conversion
Why:
- 策略调整:登录后才能访问的内容从"禁止"改为"纳入本项目范围",
  同时明确凭据管理红线(合法账号、不入 git、云服务器隔离)。
  解锁 u.lceda.cn 工程源 JSON,这是训练数据质量的关键升级。
- 计划中"存储"和"运行环境"一直模糊,现在按 Charles 提供的广州云服务器
  + 存储分级演进(Gitea LFS → 对象存储)给出清晰路径。
- 打通 oshwhub (EasyEDA) 与 bshada/open-schematics (KiCad) 两个生态,
  需要一个 EDA→KiCad 批转换脚本。先把它纳入 plan,等拿到工程源再实现。

What:
- CLAUDE.md: 登录态条款从"不抓"改为"合法账号可抓",凭据管理写死在
  ~/.secrets/,事件记 docs/secrets.md;合规红线同步更新
- plan.md §0.5: 新增 基础设施段(机器初始化 / 调度 / 登录态获取)
- plan.md §1.4: 存储分级演进(< 50 GB 云盘,50-200 GB 评估,> 200 GB 迁对象存储)
- plan.md §1.6: 登录态抓 u.lceda.cn 工程源
- plan.md §1.7: scripts/convert_to_kicad.py 批处理,候选 easyeda2kicad.py
- plan.md 风险表: 加账号封禁 / 转换失败 / 云服务器单点故障三条
- docs/sources/oshwhub.md: u.lceda.cn 从"未开放"移到"需登录,已纳入范围"
- README.md 数据源表: 加"登录态"列 + 运行环境说明
- log.md: 本次策略变更记录

未改:未新增 docs/infra.md(等机器到位 + 真实细节后再写),scripts/convert_to_kicad.py
尚未实现(等拿到工程源样本再实现)。

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

379 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**工程源 JSON 需登录**(见 §3.5 & `plan.md` §1.6 |
| 当前已入库 | 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 需登录的端点(纳入本项目范围,待 Phase 1.6 实现)
| 端点 | 现象(匿名) | 登录后预期 |
|---|---|---|
| `/api/project/<uuid>` | `{"code":104001,"success":false}` | 返回项目完整元数据(待验证) |
| `u.lceda.cn/api/projects` | HTTP 401 "尚未登录" | EasyEDA 编辑器的工程列表 |
| `u.lceda.cn/api/project/<uuid>` | 302 重定向 | **工程源 JSONschematic + PCB + 组件)— 训练数据最关键的一层** |
| `/api/search/*` | 空响应 | 可能可用(待验证) |
**策略**(按 `CLAUDE.md` "登录态"条款):使用合法账号登录后抓,账号放云服务器 `~/.secrets/`QPS ≤ 0.2 自律防封号,凭据不入 git。
实施见 `plan.md` §1.6;抓到的源 JSON 会走 `scripts/convert_to_kicad.py`§1.7)统一到 KiCad 格式。
---
## 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 需登录 | 账号被封 → 工程源获取中断 | Phase 1.6 登录爬QPS ≤ 0.2,账号隔离在云服务器 `~/.secrets/`,必要时多号轮询 |
| 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 分布)合并成本文 |