艳本资料书架:一个 Go 写的数据归档工具,和它背后的下载思路

作者 mcx 日期 2026-10-21
艳本资料书架:一个 Go 写的数据归档工具,和它背后的下载思路

日本国立国会文化研究机构(日文研)有一个公开的「艶本資料データベース」,收录了 470 部江户至明治时期的艳本资料,页面扫描件全部公开。网站是九十年代风格的 CGI 加 frameset,逐页点击可以看,整套保存下来要花不少功夫。

这个工具叫 EhonShelf,用 Go 加 MyGo 写的原生桌面应用,把整个数据库做成一个可以离线翻阅的书架。

一、阅读器

EhonShelf 书架

主界面是封面网格:书名、绘师、年代、页数。下载完成的作品显示完整封面,未完成的卡片半透明。

阅读器支持单页/双页切换、左右阅读方向切换、页码跳转、缩放(最大 6 倍)、适应窗口。键盘方向键翻页,Cmd+0 回到适应窗口。

设置页

设置页有三项配置:数据文件夹、代理、请求间隔。后两项的取值来自下面的调试过程。

二、下载思路

下载器前后改了五版,每一版解决一个具体问题。

第一版:全部请求返回 403

程序发出去的请求全部是 403。

按顺序排查:

猜测 验证方式 结果
UA 被拦 换成 Chrome UA 仍 403
Cookie 缺失 从浏览器导出完整 cookie 仍 403
TLS 指纹 用 curl_cffi 伪装 Chrome 的 JA3 仍 403
频率限制 间隔从 1 秒放到 10 秒 仍 403

同一时间浏览器打开同一个页面正常。

最后测试网络出口:

1
2
curl -x http://127.0.0.1:7897 https://lapis.nichibun.ac.jp/enp/UserMenu
# 200,16838 字节

结果:该站点按地区放行 IP,直连的国内出口一律返回 403。

改动是给 HTTP 客户端加 Transport.Proxy,同时把 403 的错误提示改成「需要代理」。

第二版:图片清晰度不一致

能连上之后,部分作品是 3000 像素宽的扫描件,部分作品只有 133×90 的缩略图。

抓一份 PDF 查看内部结构:

1
2
430_A_15.pdf  899992 字节  PDF-1.6  /JPXDecode
081_A_01.pdf 239709 字节 PDF-1.4 /DCTDecode

站点使用两种编码:普通 JPEG(/DCTDecode)和 JPEG 2000(/JPXDecode)。提取逻辑只处理前者,遇到后者返回空,程序退回缩略图。

JPEG 2000 的 Go 解码器选了纯 Go 的 mrjoshuak/go-jpeg2000,解开后是 3300×2600。

同时发现另一种降级情况:站点在高负载时返回 1182 字节的 HTML 错误页,程序把它当作 PDF 解析,同样退回缩略图。现在先检查文件头是否为 %PDF,不符合就重试。

第三版:等待时间过长

测得 1 秒/次的请求连续 8 到 10 次会触发封禁,于是设置封禁后固定等待 10 分钟。

两小时下载 6 部,日志里 13 次封禁。同期手动请求被拒的页面,返回 200 的时间远早于 10 分钟。

改为边等待边探测,每 30 秒请求一次:

1
2
3
19:04:07 HTTP 403 /enp/image/1/264_B_07.pdf
19:04:07 被封,每 30s 探测一次,最多等 10m;第 1/12 次
19:04:37 封禁已解除,继续

同样一次封禁,耗时从 10 分钟降到 30 秒。

第四版:探测请求本身失败

改完之后仍然卡住。程序探的是被拒的那一页,手动用 curl 请求返回 200,程序自己的探测一直返回 403。

原因是探测复用了爬虫的 HTTP 客户端,它的 cookie jar 和连接池保留了被拒绝时的状态。

改动是探测使用新建的临时客户端,探测成功后重置爬虫的 jar。

第五版:无界面全量下载

全量下载使用 -fetchall 模式,直接写入 NAS:

1
./ehonshelf -fetchall -out /Volumes/TO3TNS/EhonShelf -proxy http://127.0.0.1:7897

设计要点:

  • 断点续传:每部作品一个目录,meta.json 记录页数,已存在的页跳过。
  • 逐页落盘:每完成一页写入一次 meta.json,长篇作品中途中断不用从头开始。
  • 失败集中重试:全部跑完后重新扫描失败的 id,这类失败多为限流,重试即可通过。
  • 日志完整:每行带时间戳,封禁、探测、恢复都有记录。

三、调试过程中的几个判断

这几次排查的共同点是:第一眼看到的症状和实际原因不一致。

403 的初判是 UA 或 cookie,实际是 IP 归属;图片模糊的初判是站点分辨率有限,实际是编码格式未识别;下载慢的初判是限流严格,实际是等待策略过长。

另外有一次改完代码没有重新编译,程序运行的仍是两小时前的二进制,排查方向因此偏了一段时间。现在每次修改后会先核对二进制文件的时间戳。


项目地址:github.com/mcxen/ehon-shelf