油猴脚本实战:Fantia图片批量下载器的设计原理与踩坑记录
简介面向Fantia创作者与订阅者这款JavaScript编写的油猴脚本能在图片框上直接生成下载按钮一键将整套图片自动打包为ZIP保存解决批量收藏时逐张另存效率低下的痛点。资源压缩包仅3KB共2个文件其中主脚本负责注入按钮与流式打包配套Markdown说明文档则写清了安装步骤、使用方式及浏览器兼容性提醒。已有10511人学习使用说明其在相关人群中具备实用价值。脚本推荐在Firefox中运行因Chrome跨域加载图片存在CORS问题文档中对这一坑点做了明确提示同时附有GitHub源码地址方便开发者阅读实现逻辑、自行修改或提交问题对想了解油猴脚本与ZIP生成技巧的前端爱好者也有参考意义。 用 Fantia 的朋友大概率都经历过这种手酸时刻喜欢的创作者连着更新好几组作品每张都想存得先点开原图、右键、另存为、换个目录再回来点下一张。要是哪天更新了 30 张图这半小时基本就交代了而且还不保证存到的是原图还是压缩水印版。我写这个 Fantia-Downloader 油猴脚本就是为了把这段重复劳动彻底砍掉装上 Tampermonkey把脚本挂上去打开帖子点一下页面里的图片和附件就能批量落盘文件名自动按规则排好下载完直接进文件夹整理。这个项目本身不复杂但做下来踩了不少坑。这篇文章会把脚本的设计思路、核心实现逻辑、以及在真实页面环境下遇到的那些文档里不会写的问题全部摊开讲一遍。适合两类人看一类是正在用或想用类似脚本的读者可以了解它到底是怎么工作的、哪些地方容易出问题另一类是自己想写油猴脚本处理其他平台下载需求的开发者这套思路换个选择器、改改匹配规则就能复用。1. 为什么是油猴脚本而不是专门做一个下载器先回答一个很多人会问的问题Fantia 的内容保存需求市面上不是没有通用下载器为什么要用油猴脚本自己造轮子1.1 登录态和权限才是最麻烦的坎大多数独立下载器走的是你喂给我一个链接我去抓页面的模式。可 Fantia 这类创作者平台的绝大部分内容只有登录用户才看得到付费订阅部分还得带着你的购买身份才能访问。下载器一旦脱离浏览器环境就得自己去处理 Cookie、会话有效期、请求头校验这套东西做起来费劲还特别容易因为站点更新而失效。油猴脚本跑在浏览器页面里天然继承了你的登录态。脚本不需要知道你的密码也不需要手动去复制 Cookie它只是在你已经打开的页面上做信息提取文件请求身份验证这件事完全交给浏览器本身完成。看起来轻描淡写但这恰恰是脚本方案最稳的核心原因。1.2 部署成本趋近于零用户侧只需要装一个 Tampermonkey 浏览器扩展然后一键导入脚本刷新页面就能看到脚本注入的下载按钮。不需要装 Python 环境、不需要配 Node.js、不需要理解命令行参数。这对非技术用户非常友好——Fantia 的受众很多是插画、漫画、音声爱好者他们的诉求就是我要把图存下来不该卡在环境配置这一步。开发者侧的迭代成本也低。改完脚本浏览器里点一下重新加载立刻就能看到效果。不像独立下载器改一次请求逻辑还得重新打包、发版、等用户更新。1.3 它的边界只处理你本来就能看到的内容需要强调一点这类脚本的本质是将页面上已授权给你展示的内容以本地方便管理的格式保存下来它不是破解工具。免费公开内容、你已经订阅过的付费内容、创作者公开的素材包这些都属于页面允许你访问的范围。脚本不负责绕过任何权限校验也做不到绕过——它拿到的数据跟你在浏览器里亲眼看到的是同一份。把边界划清楚后续做功能时才不会往歪路上走。2. 脚本核心架构从页面 DOM 到磁盘文件整个脚本的逻辑链路可以拆成四段注入页面、收集目标文件地址、构造下载任务、按顺序落盘。每一段都有独立的策略选择和隐藏细节。2.1 注入时机决定了一半的稳定性脚本通过用户脚本元数据声明匹配规则// UserScript // name Fantia Downloader // namespace fantia-downloader // version 1.0.0 // description 抓取 Fantia 帖子页面中的图片与附件文件 // author you // match https://fantia.jp/* // grant GM_download // grant GM_xmlhttpRequest // run-at document-end // /UserScriptmatch用通配符覆盖整个站点域因为脚本需要同时处理帖子详情页、粉丝主页、文章归档页几种场景。run-at document-end表示在 DOM 解析完成后注入这时候页面主体结构已经存在能直接查到内容区的容器节点。但 Fantia 的很多内容是懒加载的这意味着DOM 解析完成不等于所有图片都加载了。实际开发时只靠注入时机不够脚本内部还要监听页面变化后面会专门讲。2.2 收集文件地址三条路径组合着用页面里的图片地址用最朴素的办法就能拿到一大部分function collectImageUrls(root) { const results []; const nodes root.querySelectorAll(img); nodes.forEach(img { const url img.dataset.src || img.src; if (url url.startsWith(http) !url.includes(avatar)) { results.push(url); } }); return [...new Set(results)]; }这里有个细节很多平台的图片标签会先把真实地址放在>function fetchAsBlob(url) { return new Promise((resolve, reject) { GM_xmlhttpRequest({ method: GET, url, responseType: blob, onload(res) { res.response ? resolve(res.response) : reject(new Error(empty response)); }, onerror: reject }); }); }我的实践结论是图片优先走GM_download附件文件走GM_xmlhttpRequest。原因后面在踩坑记录那一节展开。2.4 任务队列别一股脑全塞给浏览器收集到 50 个文件地址直接对每一个地址都发起下载浏览器会瞬间建立大量并发连接。表现是下载列表同时弹出几十个任务磁盘和网络双双吃紧甚至触发浏览器的并发连接限制让后半程全部失败。脚本里用一个简单队列控制并发数同一时间只跑 2 到 3 个下载任务每个任务之间加 300 到 500 毫秒的延时。这样页面不会卡死下载成功率反而更高。3. 文件命名的讲究一个看着不起眼但决定体验的环节下载脚本做了几个月后发现用户反馈最多的问题往往不是下载失败而是文件名一团乱。Fantia 页面上图片的原始文件名通常是随机哈希串比如202ab3c4d5e6f7.jpg几十张图下到文件夹里根本分不清哪张是哪张。这个问题的解法不复杂但细节非常多。3.1 命名规则帖子标题加序号推荐的文件名结构是[帖子编号] 帖子标题 - 序号.扩展名function buildFileName(postId, postTitle, index, ext) { const cleanTitle postTitle .replace(/[\\/:*?|]/g, ) // 去掉文件名非法字符 .replace(/\s/g, ) .trim() .slice(0, 80); // 限制标题长度避免超过文件系统全路径上限 return [${postId}] ${cleanTitle} - ${String(index).padStart(3, 0)}.${ext}; }去掉非法字符这步很容易漏。Windows 文件名不能用\ / : * ? |而 Fantia 的帖子标题五花八门半角问号、全角冒号全都能出现。不处理的话脚本会下载得很欢快然后浏览器弹出一堆文件名无效的错误。另外标题截断到 80 个字符是实践中的平衡值。太短会丢失辨识度太长又会让整个文件路径超过 Windows 的 260 字符限制尤其是保存嵌套目录较深的时候。3.2 序号补零解决排序问题String(index).padStart(3, 0)看起来是个小动作但很重要。如果序号是 1、2、3 …… 10、11文件夹里的排序会变成 1、10、11、2、3阅读顺序全部乱掉。补到三位数后就是 001、002、003排序永远正确。图片超过 999 张的情况基本不存在三位数足够。3.3 避免重复下载记录已保存文件清单下载不是一次性的操作。今天看了帖子存了一次过几天创作者更新了楼层内容你可能想再跑一次。如果脚本无脑把全页面重新下一遍会出现大量重复文件。我的做法是脚本在下载前把已成功的文件名列表读出来下次运行时做一次对比跳过已存在的文件。这个逻辑依托于文件名里包含的帖子编号和序号——只要命名规则稳定去重就是天然可靠的。当然本地文件系统里存一个 manifest 清单会更稳妥但日常使用中靠文件名去重已经足够省心。3.4 原图与缩略图的识别优先级页面里同一个图片内容往往存在多个尺寸版本。缩略图地址通常带有类似_thumb、_s、?w300之类的特征原图则没有或者尺寸参数更大。脚本在收集链接时需要做一轮过滤只保留质量最高的版本。一个可行的判断思路是用图片地址里是否有尺寸后缀来筛function isLikelyOriginal(url) { return !/_(thumb|small|medium|s|m)\b/i.test(url) !/w\d|h\d/.test(url); }这个正则不能覆盖所有情况每个平台的命名规则不同需要实际打开页面做一次地址对比才能确定。第一次调试脚本时建议把收集到的地址先打印到控制台人工过一遍确认哪些是原图哪些是缩略图再固化过滤规则。4. 开发实测中的坑与绕行方案这节是整个项目最有价值的部分。诚实的讲脚本的大体框架一个晚上就能写完剩下八成时间全耗在页面居然会这样的意外上。4.1 懒加载导致的下载了一片蓝天白云的图第一次测试脚本时下载下来二十多张图片打开一看全是同一张占位图——灰色的背景上有个加载中的图标。原因就是懒加载机制页面为了加速首屏渲染真实图片地址放在>const observer new MutationObserver(mutations { const newUrls collectImageUrls(document); if (newUrls.length pendingCount) { queue.add(newUrls.slice(pendingCount)); } }); observer.observe(document.body, { childList: true, subtree: true });4.2 原图地址藏在了一个不起眼的变量里有段时间发现部分帖子的胶卷式图片列表DOM 里无论如何都找不到原图地址>GM_xmlhttpRequest({ method: GET, url: imageUrl, responseType: blob, headers: { Referer: https://fantia.jp/ }, onload(res) { const objUrl URL.createObjectURL(res.response); const a document.createElement(a); a.href objUrl; a.download fileName; a.click(); URL.revokeObjectURL(objUrl); } });4.4 动态加载的楼层内容只处理首屏的假象Fantia 帖子页面里创作者可以在评论区继续发图这部分内容要点击展开按钮才会加载。如果脚本只在首屏收集一次地址会漏掉评论区里的所有文件。处理方式是脚本注入一个继续加载的辅助函数如果页面上存在展开按钮就自动点击一次等新内容渲染后再收集一轮地址。需要设置一个合理的等待时间比如展开后等待 800 毫秒再执行下一轮收集。等待时间太短新图片还没渲染完成太长又影响用户体验。4.5 文件名重复导致下载任务被覆盖Fantia 上存在多楼层帖子不同楼层里可能各有一张001.jpg。如果文件名只按序号命名后下载的文件会直接覆盖先下载的。解决办法是命名规则里加入区块标识比如楼层编号[01-003].jpg、[02-003].jpg保证不同区域的同名文件不会互相冲突。这个坑很隐蔽如果只在单楼层帖子上做测试永远发现不了。建议开发时用多楼层帖子做回归测试。5. 合理使用边界与进阶扩展方向脚本写到能稳定使用之后我花了不少时间思考它的边界和后续可以继续做的事。这里是我的实际经验供参考。5.1 版权意识下载不等于可以随意分发Fantia 上的创作者靠订阅收入生存付费内容更是他们主要的收入来源。脚本的定位应该是方便自己存档、离线浏览而不是把他人付费内容打包传播的工具。我在脚本的说明文档里也明确写了不要将下载的内容上传到公开平台不要二次销售保留创作者的水印和版权标识如果你真的喜欢某个创作者优先去原平台支持他们。这个边界不是客套话而是脚本能否长期存在的寿命线——一旦被用来做侵权分发无论是脚本还是使用者都会面临不必要的风险。5.2 功能扩展的方向进度面板、筛选下载、打包压缩当前版本的脚本已经能满足点一下全部下载的日常需求但还可以扩展几个方向第一是下载进度面板。在页面角落注入一个小面板实时显示已下载 18/42失败 2 个失败项支持单独重试。这个功能对大量文件下载特别有用能显著降低下完不知道缺没缺的焦虑感。第二是内容筛选。有些用户只想要图片不想要音频和视频有些用户只需要附件文件。脚本可以在收集完成后按扩展名做一次分类展示让用户勾选哪些类型需要下载。虽然不是必须但用户体验提升明显。第三是打包压缩。如果用户希望整个帖子保存为一个压缩包可以用 JSZip 这个库在脚本里把下载的 Blob 数据打包再一次性保存。注意这需要同时处理内存占用问题楼层内容特别多的帖子一次性全压会有卡顿风险稳妥的做法是边下载边写入控制同时驻留内存的文件数量。5.3 给后来开发者的一句话建议油猴脚本看起来简单真正稳定的版本都是在真实页面上反复磨出来的。开发时请把生产环境不同于测试环境这句话刻在脑子里页面结构会变、CDN 策略会变、浏览器权限策略会变脚本想活得久就得把地址收集、去重、命名、下载这些模块解耦开任何一个环节出问题都能独立修复。我自己的维护习惯是每次 Fantia 页面上线新功能第一时间打开脚本跑一轮测试帖子确认四件事登录态是否正常、收集的地址是否完整、命名规则是否仍然适用、下载是否全部成功。这套检查流程跑下来大概五分钟却能让脚本省下大量被动修 bug 的时间。这个项目真正让我觉得有价值的地方不是能把图下下来这个结果而是它把重复劳动一次性解决之后省下来的时间又可以花在欣赏作品本身。如果你也有类似的内容浏览习惯希望这篇文章里的设计和踩坑记录能帮你少走几段弯路。本文还有配套的精品资源点击获取

相关新闻

Font Awesome 4.7.0 实战指南:部署、用法与避坑要点

Font Awesome 4.7.0 实战指南:部署、用法与避坑要点

简介:Font Awesome 4.7.0 是一套被广泛使用的图标字体库,面向网页设计师与前端开发者,把 470 个矢量图标封装为 Web 字体,可像文字一样控制大小、颜色,并适配响应式布局,覆盖社交网络、通用对象、用户界面元…

2026/9/1 20:46:06 阅读更多 →
恒生金融IT笔试复盘:Java基础、SQL查询与数据结构全解析

恒生金融IT笔试复盘:Java基础、SQL查询与数据结构全解析

2015年秋招的时候,我投了恒生的Java开发岗。说实话,金融IT公司里,恒生的笔试算是比较有代表性的——它不像互联网大厂那样狂刷难题,也不像纯外包公司那样随便出点选择题糊弄人,而是把Java基础、数据结构、数据库SQL、逻…

2026/9/1 20:46:06 阅读更多 →
Claude Code 终端 AI 编程助手安装配置与第三方模型接入指南

Claude Code 终端 AI 编程助手安装配置与第三方模型接入指南

Claude Code 不是网页聊天框,也不是普通代码补全插件,它是能直接跑在终端里的 AI 编程助手。你可以把它放在任意项目目录里,让它读代码、改文件、执行命令,在多轮对话里完成一次真实开发任务。最近网上刷到不少标着“吴恩达”“20…

2026/9/1 20:45:05 阅读更多 →

最新新闻

AI追光免费编辑器:从文生视频到电影级短片创作全流程解析

AI追光免费编辑器:从文生视频到电影级短片创作全流程解析

如果你过去一年还在用 AI 生成单张图片,那你可能已经落后了一个版本。AI 视频生成在最近这段时间里,完成了从“能动的图”到“能讲故事的片子”的跨越。一个比较有说服力的信号是:一部叫《大梦三千》的短片,以参赛作品的身份进入了…

2026/9/1 21:22:44 阅读更多 →
从Token计费到按效付费:大模型商业化技术落地指南

从Token计费到按效付费:大模型商业化技术落地指南

近两天 AI 圈和数据要素圈最值得关注的一条消息,是国家数据局局长刘烈宏在发布会上明确提出:围绕国民经济重大场景,研究探索“词元增值订阅”“按效付费”等商业模式。这句话信息量不小。过去一年多,大模型从技术竞赛快速进入商业…

2026/9/1 21:22:44 阅读更多 →
STM32H5驱动LSM6DSV16X六轴传感器:陀螺仪轮询读取全流程详解

STM32H5驱动LSM6DSV16X六轴传感器:陀螺仪轮询读取全流程详解

简介:本资源是一套面向嵌入式初学者与STM32H5平台开发者的LSM6DSV16X六轴MEMS传感器实战入门资料,聚焦轮询模式下陀螺仪数据采集的完整实现,解决无中断依赖、低复杂度场景下的惯性传感基础验证问题。压缩包共200个文件,涵盖79个头…

2026/9/1 21:22:44 阅读更多 →
llmfit测试体系:硬件fixture与Schema验证机制完整解析

llmfit测试体系:硬件fixture与Schema验证机制完整解析

llmfit测试体系:硬件fixture与Schema验证机制完整解析 【免费下载链接】llmfit Hundreds of models & providers. One command to find what runs on your hardware. 项目地址: https://gitcode.com/GitHub_Trending/ll/llmfit llmfit 是一款本地大模型硬…

2026/9/1 21:22:44 阅读更多 →
机械键盘选购指南:从轴体结构到客制化,平衡颜值与实力

机械键盘选购指南:从轴体结构到客制化,平衡颜值与实力

最近在折腾一个自动化脚本,需要模拟键盘输入,顺手翻出了抽屉里吃灰的几把键盘。看着它们,我突然意识到一个有趣的现象:我们似乎进入了一个“键盘选择困难症”的时代。一边是CHERRY这类老牌厂商,用经典轴体和扎实做工告…

2026/9/1 21:22:44 阅读更多 →
自动驾驶算法岗笔试通关:图森2020校招卷解析与备考指南

自动驾驶算法岗笔试通关:图森2020校招卷解析与备考指南

每年一到校招季,就会有不少同学来问我怎么准备自动驾驶公司的算法岗笔试。图森未来2020校招笔试卷(三)这份卷子在我接触过的几家里算是蛮有代表性的,它不像某些大厂那样整张卷子全是LeetCode原题,也不会像偏研究型团队…

2026/9/1 21:21:43 阅读更多 →

日新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/1 0:03:21 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/1 0:03:21 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/1 0:03:21 阅读更多 →

周新闻

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

每年校招季我都会接触不少准备数据库方向笔试的同学,看到最多的状态就是:简历上写着“熟悉 MySQL”“了解索引优化”,一碰到数据库管理工程师的笔试卷,却在索引、事务、锁、备份恢复这些题目上翻车。网易这套 2018 校园招聘数据库…

2026/9/1 19:44:48 阅读更多 →
数字电路时序基石:深入理解建立时间与保持时间

数字电路时序基石:深入理解建立时间与保持时间

1. 这不是“背公式”的事:时间参数到底在约束什么你翻过数字电路教材,一定见过这两个词:建立时间(Setup Time)和保持时间(Hold Time)。它们常被并列写在触发器(Flip-Flop&#xff09…

2026/9/1 18:13:19 阅读更多 →
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

1. 项目缘起:从赛题到超声波测距机的诞生第八届蓝桥杯单片机设计与开发国赛的题目,我至今记忆犹新。它没有直接给出一个花哨的名字,而是用“超声波测距机”这个朴实无华的功能描述,精准地勾勒出了考核的核心。对于当时备赛的我而言…

2026/8/31 14:32:14 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/1 0:03:21 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/1 0:03:21 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/1 0:03:21 阅读更多 →