Files
FacereDataset/docs/sources/pro_crawl_vs_export.md
Knowit fc2a45f658 docs: explain per-doc .epro2 crawl vs web-export .epro2 ZIP
Colleague-facing explainer at docs/sources/pro_crawl_vs_export.md.
Addresses the "I see 278 .epro2 files but my browser only downloaded
one" confusion: web download is a ZIP container (extension is a UX
choice, not a format), our crawl produces per-doc message streams.
Both carry equivalent EPRO2 data; only real gap is IMAGE/ binary
previews which we don't fetch yet.

Why per-doc and not ZIP: the ZIP path has no public endpoint —
three HARs confirm the export button fires zero HTTP requests, it's
pure client-side JSZip on data already loaded by the editor. Our
crawler hits the same chain endpoints the editor uses internally,
which delivers per-doc streams.

Log entry references the 278 vs 266 doc-count delta for ESP-VoCat
(we walk full history chain, web export is a current snapshot).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 00:13:52 +08:00

7.0 KiB
Raw Blame History

为什么爬取产出 N 个 .epro2 文件,而网页端导出只有一个 .epro2

给同事的科普向解释。如果你看到我们仓库里某个 Pro 项目目录下躺着几十到上千个 .epro2 而你自己从网页端"下载工程包"只拿到一个 .epro2,你不是抓错了,它们也不是同一种文件, 只是名字撞了。


一句话结论

网页端下载的那个 .epro2 是 ZIP 压缩包(扩展名误导),里面其实有三样东西: 工程消息流 + 工程元数据 + 嵌入图片。 我们爬取产出的 N 个 .epro2 是工程内每个"文档"各自的消息流 一个组件一文件、一张原理图一文件、一块 PCB 一文件,没打包。

两者承载的数据是等价的,只是打包方式相反——一个是"全压一坨" 一个是"每文档一个文件"。


拿 ESP-VoCat 做实例

同一个项目(ba64bd6f...),两种获取方式得到的东西:

网页端"下载工程包" → ProPrj_ESP-VoCat.epro21 个文件1.4 MB

文件名虽然是 .epro2实际是 ZIPunzip -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
FOOTPRINTPCB 封装) 55 55
SCH_PAGE原理图分页 9 6
SCH原理图容器 6 3
PCBPCB 容器) 6 3
BOARD板子顶层 6 3
BLOB / FONT / CONFIG 1 / 1 / 1 1 / 1 / 1
合计 278 266

那 N 个文件到底是啥?为什么这么多

EasyEDA Pro 是文档式工程——跟 KiCad「整个项目两个大文件」的思路不一样

  • KiCad1 个 .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/<P>/branches/<B>/histories/<H> 拿 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 网页端 ZIPIMAGE/ 目录)

TL;DR

  • "网页端 .epro2" = ZIP 容器(叫 .epro2 是 UX 决定,不是格式)
  • "爬取的 .epro2" = 工程内每个文档自己的 EPRO2 消息流
  • 两者信息量基本等价,包装方式相反
  • 我们选 per-doc 是因为 ZIP 这条路根本没有服务端端点可爬,并且 per-doc 对下游处理更友好
  • 唯一真实差异是 IMAGE/ 二进制图(我们暂未爬),其它都对得上