diff --git a/docs/sources/pro_crawl_vs_export.md b/docs/sources/pro_crawl_vs_export.md new file mode 100644 index 0000000..e449193 --- /dev/null +++ b/docs/sources/pro_crawl_vs_export.md @@ -0,0 +1,166 @@ +# 为什么爬取产出 N 个 .epro2 文件,而网页端导出只有一个 .epro2 + +> 给同事的科普向解释。如果你看到我们仓库里某个 Pro 项目目录下躺着几十到上千个 `.epro2`, +> 而你自己从网页端"下载工程包"只拿到一个 `.epro2`,你不是抓错了,它们也不是同一种文件, +> 只是名字撞了。 + +--- + +## 一句话结论 + +**网页端下载的那个 `.epro2` 是 ZIP 压缩包**(扩展名误导),里面其实有三样东西: +工程消息流 + 工程元数据 + 嵌入图片。 +**我们爬取产出的 N 个 `.epro2` 是工程内每个"文档"各自的消息流**, +一个组件一文件、一张原理图一文件、一块 PCB 一文件,没打包。 + +两者**承载的数据是等价的**,只是打包方式相反——一个是"全压一坨", +一个是"每文档一个文件"。 + +--- + +## 拿 ESP-VoCat 做实例 + +同一个项目(`ba64bd6f...`),两种获取方式得到的东西: + +### 网页端"下载工程包" → `ProPrj_ESP-VoCat.epro2`(1 个文件,1.4 MB) + +文件名虽然是 `.epro2`,**实际是 ZIP**。`unzip -l` 看里面: + +``` +IMAGE/ (目录) +IMAGE/38cc57d9...webp 60 KB ─┐ +IMAGE/450927588f1f...webp 260 KB │ +IMAGE/78404fce3de8...webp 134 KB │ 6 张嵌入图片 +IMAGE/9904a7de7784...webp 48 KB │ (组件预览/3D 缩略图) +IMAGE/a20de4ce4ca4...webp 141 KB │ +IMAGE/b3294b3234d6...webp 286 KB ─┘ +project2.json 25 KB 工程元数据 +ESP-VoCat 喵伴...epru 6.25 MB 全部 doc 拼成一坨的消息流 +``` + +### 我们爬取产出 → `data/raw/oshwhub/ba64bd6f.../source/`(278 个文件,7.5 MB) + +每个 doc 一个独立的 `.epro2`: + +``` +00184cbbad5a8d33.epro2 每个 SYMBOL 一个文件 × 105 +0a29a31811039d37.epro2 每个 FOOTPRINT 一个 × 55 +037c... .epro2 每个 DEVICE 一个 × 88 +... 总共 278 个 .epro2 文件 +manifest.json 索引(哪个文件对应哪个 docType) +structure.json 工程结构树(哪些 sheet / pcb / board 属于哪些 board) +``` + +按 docType 分布看: + +| docType | 我们爬取 | 网页端导出 | +|---|---:|---:| +| SYMBOL(原理图符号) | 105 | 105 | +| DEVICE(元件 BOM 元数据) | 88 | 88 | +| FOOTPRINT(PCB 封装) | 55 | 55 | +| SCH_PAGE(原理图分页) | 9 | 6 | +| SCH(原理图容器) | 6 | 3 | +| PCB(PCB 容器) | 6 | 3 | +| BOARD(板子顶层) | 6 | 3 | +| BLOB / FONT / CONFIG | 1 / 1 / 1 | 1 / 1 / 1 | +| **合计** | **278** | **266** | + +--- + +## 那 N 个文件到底是啥?为什么这么多 + +EasyEDA Pro 是**文档式工程**——跟 KiCad「整个项目两个大文件」的思路不一样: + +- KiCad:1 个 `.kicad_sch` + 1 个 `.kicad_pcb`,组件库走外部引用 +- EasyEDA Pro:**每用到一种组件,就把它的 symbol / footprint / device 各自快照成独立文档存进工程里**, + 外加多页原理图、容器层、全局资源…… + +ESP-VoCat 用了大约 100 种不同的元件(ESP32-WROOM、各种规格的电阻、按键、LED、晶振、屏幕模组……), +所以**自动产生 248 条都是元件库快照**:105 SYMBOL + 55 FOOTPRINT + 88 DEVICE。 +剩下 30 条才是工程"主体"(9 张原理图 + 6 套 PCB/BOARD/SCH 容器 + FONT/BLOB/CONFIG 全局资源)。 + +> **直觉对照**:把 EPRO2 想象成**每个文档的 git history**,不是项目压缩包。 +> 一个 SYMBOL 文件 ≈ 立创元件库里"这颗 0603 电阻"的快照 + 你在工程里对它的每次微调。 + +每个 EPRO2 文件内部是**事件流**(event-sourced),按行排: + +``` +{"type":"DOCHEAD","ticket":4456}||{"docType":"FOOTPRINT","uuid":"a20de4ce..."}| +{"type":"OP","op":"ADD","id":"e123",...}||{...payload...}| +{"type":"OP","op":"UPDATE","id":"e123",...}||{...payload...}| +... +``` + +回放这些 event 就能拿到文档的当前状态。 + +--- + +## 为什么数量对不上(278 vs 266) + +我们多 12 个,全部集中在**容器层文档**:SCH_PAGE 多 3、SCH 多 3、PCB 多 3、BOARD 多 3。 + +原因很简单: + +- **网页端导出**是当前快照——只导"这个项目此刻包含哪些 doc" +- **我们的爬虫**走完整 history chain——把工程历史上**演化路径**上出现过的 doc 都拉下来了 + (比如项目历史里"加过一张图、又删了",那张图的 SCH_PAGE 在我们这里有,在网页导出里没有) + +元件库部分(248 条)完全一致,没有差异。 + +--- + +## 体积对比 + +| 形态 | 大小 | 说明 | +|---|---:|---| +| 网页 ZIP 压缩态 | **1.41 MB** | 适合用户下载 | +| 网页 ZIP 解压(.epru + json + 6 图) | 7.20 MB | | +| 我们爬取(278 个独立 .epro2) | **7.85 MB** | 多 12 容器 + 缺 IMAGE/ | + +> 我们多 0.65 MB 是那 12 个历史容器层;他们多的部分是 6 张 IMAGE/ 预览图(~0.93 MB), +> 这是我们当前**确实缺的一块数据**——blob 引用爬到了,二进制图片本体没爬。 +> 不影响 EPRO2 → KiCad 转换语料生成(KiCad 端没对应字段),但如果要做"原貌可视化还原"会需要补。 + +--- + +## 为什么我们走 N 文件而不是 1 ZIP + +简单说:**ZIP 这条路根本没有公开端点可爬**。 + +- 三份 HAR 反复抓过编辑器流量,**网页端"导出"按钮压根不发任何 HTTP 请求** +- 它是纯前端 JS 操作:编辑器把已经加载到内存的数据用 `JSZip` 在浏览器里现拼现压 +- 所以服务端没有 `/export` 之类的 endpoint 给爬虫调用 + +我们走的是**官方编辑器加载工程时用的同一套 API**(`/api/v4/projects/

/branches//histories/` 拿 chain, +逐段 AES-GCM 解密 + gunzip + 按 DOCHEAD 切分),所以拿到的是"原汁原味"的 per-doc EPRO2 流。 + +附带好处: + +| | per-doc 爬取 | 网页 ZIP | +|---|---|---| +| 单文档 diff | ✅ 容易(每文件独立) | ❌ 全在一坨流里 | +| 增量更新 | ✅ 只重抓变动 doc | ❌ 整包重下 | +| LFS 友好 | ✅ 单文件可独立寻址 | ❌ 一个大 zip 改一字节 LFS 也得重传 | +| 历史完整性 | ✅ 全 chain | ❌ 只有当前快照 | +| 体积 | 7.5 MB 平铺 | 1.4 MB 压缩态 | + +--- + +## 你应该用哪种 + +| 用途 | 选哪种 | +|---|---| +| 训练 LLM 做 EDA 任务、做 EPRO2 → KiCad 转换、做 BOM 抽取 | 我们爬的 per-doc | +| 给立创的人复刻你的工程(直接拿去用编辑器打开) | 网页端 ZIP | +| 想看项目历史演化、做版本 diff | 我们爬的 per-doc(含 history) | +| 想拿到组件预览图(原始 webp) | 网页端 ZIP(IMAGE/ 目录) | + +--- + +## TL;DR + +- "网页端 .epro2" = ZIP 容器(叫 .epro2 是 UX 决定,不是格式) +- "爬取的 .epro2" = 工程内每个文档自己的 EPRO2 消息流 +- 两者**信息量基本等价**,包装方式相反 +- 我们选 per-doc 是因为 ZIP 这条路根本没有服务端端点可爬,并且 per-doc 对下游处理更友好 +- 唯一**真实差异**是 IMAGE/ 二进制图(我们暂未爬),其它都对得上 diff --git a/log.md b/log.md index 47dd79f..ab67164 100644 --- a/log.md +++ b/log.md @@ -4,6 +4,22 @@ --- +## 2026-04-29 01:00 科普文档:爬取 per-doc .epro2 vs 网页端 .epro2 ZIP 整包 + +**Claude 会话** + +接 chain replay sleep 优化(commit `1e06ba6`)后续。同事看到 `data/raw/oshwhub//source/` 下面躺着 278 个 `.epro2` 而不是 1 个,会直觉以为抓错了——他们认知里的 `.epro2` 是网页端"下载工程包"那个 1.4 MB 单文件。 + +实际上: +- **网页端 `.epro2`** = ZIP 容器(扩展名误导),里面 `.epru`(拼成一坨的 EPRO2 流)+ `project2.json` + `IMAGE/` 6 张组件预览图 +- **爬取 `.epro2`** = 工程内每个文档(SYMBOL / FOOTPRINT / DEVICE / SCH_PAGE / PCB ……)自己的 EPRO2 消息流,per-doc 一文件 +- 两者**信息量基本等价**(ESP-VoCat 我们 278 vs 网页 266,多的 12 个是 history chain 上演化掉的容器层旧版本);唯一真实差异是 IMAGE/ 二进制图(我们 blob 引用爬到了但没拉本体——已知 gap) +- 我们走 per-doc 不走 ZIP 的硬约束:**ZIP 那条路服务端没有公开端点**,是纯前端 JS 现拼现压(三份 HAR 验证:导出按钮零 HTTP 流量) + +写到 `docs/sources/pro_crawl_vs_export.md`,给同事看。结构:TL;DR → ESP-VoCat 实例 → docType 分布对比表 → 数量差异解释 → 体积对比 → 选型决策表。 + +--- + ## 2026-04-29 00:30 KiCad 导出 Phase 3 hierarchical:root + global_label + 5-Voltage 电源端口 **Claude 会话**