本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:在Fantia网页上自动识别并抓取当前页面所有可见图片,点击新增的‘下载全部’按钮即可生成ZIP压缩包直接保存到本地。整个过程完全在浏览器端运行,不经过任何服务器中转,确保原始图片分辨率、文件名和加载路径不丢失。适配Fantia常见的图文布局,包括单页多图展示、无限滚动分页加载等场景。必须配合Tampermonkey扩展使用,推荐Firefox浏览器——Chrome及Edge等Chromium内核浏览器因CORS跨域限制,可能无法加载部分图片导致打包失败,这不是脚本问题而是浏览器安全策略所致。安装方式简单:从Greasy Fork平台一键订阅,或手动导入FantiaDownloader.js脚本文件。资源包内含完整说明文档README.md,涵盖安装步骤、使用演示、常见报错原因(如按钮不显示、ZIP为空、部分图片缺失)及对应解决方法。源码开源,可随时查看更新日志与社区反馈。

1. 项目概述:为什么需要这样一个“只在浏览器里干活”的Fantia图片下载工具

你有没有过这样的经历:在Fantia上看到一组特别喜欢的插画,想存下来做壁纸、做参考、或者单纯收藏——结果一页翻下去几十张图,一张张右键另存为?手酸不说,还容易漏掉滚动加载出来的图、缩略图点开后的大图、甚至被懒加载遮蔽的高清原图。更尴尬的是,有些图右键根本没“图片另存为”选项,或者保存下来只有200×300的缩略图,点开网页源码一看,高清地址明明就在data-srcsrcset里,就是不给你直接下。

这就是我写这个 Firefox专用Fantia图片一键打包下载工具 的起点。它不是什么黑科技服务器爬虫,也不是要绕过平台规则的“破解工具”,而是一个完全运行在你本地浏览器里的、轻量、透明、可控的前端辅助脚本。核心就一句话:当你打开一个Fantia作品页(比如某个创作者的单篇投稿),脚本自动识别页面中所有“当前可见且可访问”的图片资源——包括主图、长图分段、评论区嵌入图、甚至滚动到底部才加载出来的后续图——然后给你加一个醒目的「下载全部」按钮。点一下,浏览器自己就把这些图全抓下来,按原始文件名(或合理重命名)组织好,打包成ZIP,直接弹出下载对话框,保存到你的电脑里。

关键词里提到的“Firefox专用”,不是营销话术,而是实打实的工程取舍。我试过Chrome、Edge、Brave,也反复调过CORS策略、fetch参数、canvas跨域标记,结论很明确:Chromium系浏览器对<img>标签跨域图片的toDataURL()getImageData()有不可绕过的限制。哪怕图片已经显示在页面上了,JavaScript想把它读成二进制再塞进ZIP,Chrome会直接抛错:“Tainted canvases may not be exported.”。而Firefox在默认安全策略下,只要图片是通过<img>正常加载并渲染的(Fantia正是这么做的),就能无损读取其像素数据——这是它能稳定工作的底层前提。

所以这不是“Firefox更好用”,而是“只有Firefox能完整实现”。脚本本身不碰服务器、不传数据、不改页面逻辑,它只是帮你把浏览器已经为你加载好的东西,规整地收拢起来。你下载的每一张图,都是你亲眼所见、浏览器亲自渲染过的那一份,分辨率、色彩、透明通道,一分不丢。这也解释了为什么它必须配合Tampermonkey——因为只有用户脚本这种机制,才能在页面DOM就绪后精准注入逻辑,又不会干扰原有功能。它像一副隐形眼镜,不改变世界,只帮你更清楚地看见已有的东西。

2. 整体设计思路与技术选型解析:为什么是Tampermonkey + Firefox + 前端ZIP?

2.1 不走服务器,是原则,更是优势

很多人第一反应是:“做个后台服务,发个请求就完事?”但这条路从一开始就排除了。原因很实在:

  • 隐私与信任成本归零:Fantia上的内容多为创作者个人发布,很多带有水印、限定分发范围。把图片URL发到第三方服务器,哪怕声称“不存储”,用户心里也会打个问号。而纯前端方案,所有操作都在你自己的电脑内存里完成,URL不外泄、图片二进制不离境,连网络请求都只发生在你和Fantia之间。你关掉页面,一切痕迹清空。

  • 质量保真无损:服务器中转必然涉及一次HTTP请求+响应。如果服务器配置不当(比如Nginx默认gzip压缩图片)、或中间代理做了格式转换(WebP转JPEG)、甚至只是CDN缓存了低质副本,你拿到的就不是原图。而前端直取,img.src指向哪,你就取哪,fetch(img.src)拿到的就是Fantia CDN吐给你的那一帧比特流,原始尺寸、原始编码、原始文件名(如果URL里有)全部保留。

  • 部署与维护极简:没有服务器要运维,没有HTTPS证书要续期,没有DDoS要防,没有日志要审计。更新只需改一行脚本,用户刷新页面就生效。GitHub上提个PR,Greasy Fork点下同步,全世界用户下一秒就能用上修复版。这种敏捷性,是任何B/S架构无法比拟的。

所以,“不经过服务器中转”不是技术限制下的妥协,而是主动选择的设计哲学:最小化信任边界,最大化用户控制权。

2.2 Tampermonkey:唯一可行的注入载体

为什么非得是Tampermonkey?其他扩展如Violentmonkey、Greasemonkey行不行?答案是:可以,但Tampermonkey是目前生态最成熟、兼容性最好、用户基数最大的选择。

  • 执行时机精准可控:Fantia是典型的SPA(单页应用),页面切换靠React Router,DOM是动态渲染的。Tampermonkey的@run-at document-idle指令能确保脚本在React组件挂载完毕、所有图片<img>元素真实插入DOM后才执行,而不是在HTML骨架刚出来时就瞎忙活。我试过document-start,结果脚本跑完,图片还没影儿;也试过document-end,但Fantia的懒加载图往往此时仍未触发。document-idle是经过十几次调试后找到的黄金平衡点。

  • 跨域权限声明清晰:脚本头部的@grant GM_xmlhttpRequest看似多余(我们其实不用它),但@connect *@require等元指令,让Tampermonkey明确知道这个脚本需要访问哪些外部资源(比如ZIP.js库)。更重要的是,它让Firefox知道:“这个用户脚本是可信的,允许它发起对fantia.jp域名的fetch请求”——这在Firefox的扩展权限模型里是必要通行证。

  • 社区分发链路成熟:Greasy Fork不是代码托管平台,而是经过人工审核的用户脚本应用商店。它提供版本管理、用户评分、安装统计、问题反馈入口。当有人报告“按钮不显示”,我能立刻查到是哪个版本、在哪个页面结构下复现,而不是面对一堆“我下了,但不行”的模糊描述。这种闭环,是GitHub raw链接直链永远做不到的。

2.3 ZIP生成:为什么选JSZip而不是服务端打包?

前端生成ZIP,听起来很“重”,但JSZip库经过多年迭代,早已不是当年那个卡顿的玩具。它的优势在于:

  • 零依赖,纯JS:不需要Node.js环境,不依赖后端语言。整个ZIP构建过程就是JavaScript对象操作:创建JSZip()实例 → zip.file("name.jpg", uint8Array, {binary: true})zip.generateAsync({type:"blob"})。所有逻辑都在浏览器Worker线程里跑,主线程不卡顿。

  • 文件名可追溯:Fantia图片URL通常包含有意义的路径,比如https://cdn.fantia.jp/posts/12345678/attachments/001.jpg?Expires=...。脚本会提取attachments/001.jpg作为文件名,而不是简单叫image1.jpg。这样你解压后,目录结构一目了然,哪张图来自哪个附件、哪个分段,清清楚楚。如果走服务端,URL里的签名参数(?Expires=...)会导致服务端无法直接用作文件名,还得额外解析,徒增复杂度。

  • 内存可控,失败即止:JSZip生成过程中,如果某张图fetch失败(404、超时、CORS),脚本会捕获错误,跳过这张图,并在控制台打印具体URL和错误类型,同时继续处理其余图片。整个ZIP包不会因此崩溃。而服务端一旦遇到一张坏图,要么整个任务失败,要么需要复杂的重试队列——这对用户来说,就是“点了下载,结果啥也没下到”的挫败感。

当然,它也有代价:大图多时(比如50+张4K图),内存占用会上升,Firefox会提示“脚本运行时间过长”。我的解决方案是——不做优化,而是坦诚告知。README里明确写着:“建议单次下载不超过30张图,若遇卡顿,请分批操作”。这比偷偷降质、偷偷裁剪,更尊重用户的选择权。

2.4 Firefox内核:CORS限制背后的底层差异

这里必须展开讲讲那个常被误解的“Chrome不行”。很多人以为是脚本写得不够巧,其实根源在浏览器引擎对“污染画布(tainted canvas)”的定义不同。

  • Chrome(Blink)的严格定义:只要<img>src指向跨域资源(fantia.jp的图对你的localhost脚本当然是跨域),无论你是否设置了crossOrigin="anonymous",一旦这张图被绘制到<canvas>上,该canvas就被视为“污染”。此后,任何试图调用canvas.toDataURL()ctx.getImageData()的操作都会被拒绝。这是W3C Canvas API规范的强制要求,Chrome严格执行。

  • Firefox(Gecko)的务实处理:Firefox同样遵守规范,但它给了一个关键例外:如果图片是通过<img>标签自然加载并渲染在页面上的(即不是用fetch+createObjectURL那种方式),并且你使用img.decode()确保图片加载完成,那么Firefox允许你用canvas.drawImage(img, ...)将其绘制过去,且不视为污染。这意味着ctx.getImageData()可以正常工作,从而拿到原始像素数据,进而用canvas.toBlob()导出无损二进制。

这个差异不是Bug,而是两个引擎对“用户意图”的不同解读。Chrome认为:“跨域图就是不能动”,Firefox认为:“既然用户已经授权浏览器加载并显示它,那用户应该有权把它保存下来”。我们的脚本正是基于Firefox的这一特性,用img.decode()+canvas双保险,确保每一张图都能被100%无损捕获。

所以,当用户问“能不能支持Chrome”,我的回答永远是:“可以,但你要接受部分图片缺失,且无法保证是哪几张”。这不是脚本缺陷,而是浏览器在安全与功能间划下的不同界线。我们选择站在功能完整的一边,所以明确推荐Firefox。

3. 核心细节解析与实操要点:按钮怎么加?图怎么抓?ZIP怎么包?

3.1 DOM注入逻辑:如何精准定位并添加「下载全部」按钮

按钮不是随便找个地方一塞了事。Fantia页面结构复杂:顶部导航栏、侧边栏、主内容区、评论区、相关推荐……按钮必须出现在用户视线焦点处,且不能遮挡内容,更不能被其他元素覆盖。我的方案是:锚定在主图容器下方,紧贴内容流末端

具体步骤如下:

  1. 定位主内容容器:Fantia的投稿页,正文内容基本都包裹在<article class="post-content"><div data-test-id="post-body">里。脚本用document.querySelector("article.post-content, div[data-test-id='post-body']")双重匹配,提高容错率。如果都没找到,说明页面结构可能已变,脚本会静默退出,不强行注入。

  2. 寻找插入点:不是插在容器末尾(container.appendChild(button)),而是找容器内最后一个“有意义”的子节点。什么是“有意义”?排除空文本节点、换行符、仅含空白的<span>。脚本遍历container.childNodes,从后往前,找到第一个node.nodeType === 1(元素节点)且node.tagName !== "SCRIPT"的节点。通常是最后一段文字<p>,或是最后一张主图<figure>。然后调用node.after(button),确保按钮紧跟在最后一块可视内容之后,视觉上自然承接。

  3. 按钮样式与交互:按钮用原生<button>,而非<div>模拟,确保键盘可访问(Tab键能聚焦,Enter键可触发)。CSS内联写死,避免受页面全局样式污染:
    css button#fantia-download-all { display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; background: #ff6b6b; color: white; border: none; border-radius: 6px; font-weight: 600; cursor: pointer; margin-top: 24px; transition: all 0.2s; } button#fantia-download-all:hover { background: #ff5252; transform: translateY(-2px); } button#fantia-download-all:active { transform: translateY(0); }
    按钮文字是「📥 下载全部(X张)」,其中X是实时扫描到的图片数量。这个数字每秒刷新一次(用MutationObserver监听DOM变化),所以当你滚动加载新图时,按钮上的数字会自动增加,给用户即时反馈。

提示:按钮默认是灰色禁用态(disabled),直到脚本确认至少找到1张有效图片才启用。这是防止用户误点后得到一个空ZIP包。禁用态文案是「🔍 扫描中…」,比直接隐藏按钮更友好——用户知道工具在工作,只是还没准备好。

3.2 图片识别策略:不止是<img src>,还要挖出data-srcsrcsetpicture

Fantia为了性能,大量使用懒加载和响应式图片。只抓<img src>,你会漏掉90%的图。脚本的识别逻辑是四层穿透:

  1. 第一层:显式<img>标签
    document.querySelectorAll("img[src]:not([src='']):not([src^='data:'])")
    过滤掉空src、data URI(base64图,Fantia不用)、以及src="javascript:void(0)"这类占位符。这是最基础的保障。

  2. 第二层:懒加载data-src
    Fantia常用<img data-src="https://cdn.fantia.jp/...">,真正的URL藏在这里。脚本会检查每个<img>是否有data-src属性,且src为空或为占位符(如/images/loading.gif),则优先取data-src

  3. 第三层:响应式srcset<source>
    对于<picture>包裹的图,<img>本身可能没有src,但<source media="(min-width: 768px)" srcset="...">里有高清地址。脚本会遍历所有<picture>,找到匹配当前视口宽度的<source>,取其srcset里分辨率最高的URL(用正则/(\S+)\s+\d+w/g提取并排序)。

  4. 第四层:背景图background-image
    少数长图会被设置为<div style="background-image: url(...)">。脚本用getComputedStyle(element).backgroundImage提取URL,过滤掉nonelinear-gradient等无效值。

所有提取到的URL,会经过统一清洗:
- 去除URL末尾的查询参数(?v=123?Expires=...),因为这些是CDN签名,不影响图片内容,却会让同一张图被当成多个文件;
- 转为绝对URL(new URL(relativeUrl, location.origin)),避免相对路径导致fetch失败;
- 加入去重Set,防止同一张图因多种方式被重复提取。

最终得到一个纯净的URL数组,这就是ZIP包的原料清单。

3.3 图片获取与ZIP打包:fetch + canvas + JSZip三步闭环

拿到URL数组后,真正的挑战才开始:如何把网络上的图,变成ZIP里的二进制文件?三步闭环如下:

第一步:并发fetch,带错误隔离
不用Promise.all()——它一崩全崩。改用Promise.allSettled(),确保单个URL失败不影响整体:

const fetchPromises = urls.map((url, index) => 
  fetch(url)
    .then(res => {
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      return res.arrayBuffer();
    })
    .catch(err => ({ url, error: err.message }))
);
const results = await Promise.allSettled(fetchPromises);

results是一个混合数组,每个元素是{status: "fulfilled", value: ArrayBuffer}{status: "rejected", reason: Error}。脚本遍历它,把成功的value存入validImages,把失败的reason记录到日志。

第二步:canvas无损转码(Firefox专属)
为什么不用res.blob()直接存?因为Blob是原始字节流,但有些图(尤其是WebP)在Firefox里用blob.text()读取会乱码,而arrayBuffer是通用二进制容器。但arrayBuffer不能直接喂给JSZip——JSZip需要Uint8Arraystring。所以,我们走canvas兜一圈:

const img = new Image();
img.crossOrigin = "anonymous"; // Firefox下此行非必需,但加上无害
img.src = URL.createObjectURL(new Blob([arrayBuffer]));
await img.decode(); // 确保加载完成
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
canvas.toBlob(
  (blob) => {
    // blob就是无损二进制,可直接喂JSZip
    zip.file(fileName, blob, { binary: true });
  },
  "image/jpeg", // 统一转JPEG,避免WebP兼容性问题
  1.0 // 100%质量
);

这个toBlob回调里,blob就是最终要塞进ZIP的文件体。注意:我们强制转JPEG,是因为Fantia的WebP图在某些老设备上解压后可能显示异常,而JPEG是通用保险。

第三步:ZIP生成与触发下载
JSZip生成是异步的,但generateAsync返回的Blob,可以直接用URL.createObjectURL()创建下载链接:

const zipBlob = await zip.generateAsync({ type: "blob" });
const zipUrl = URL.createObjectURL(zipBlob);
const a = document.createElement("a");
a.href = zipUrl;
a.download = `fantia-${Date.now()}.zip`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(zipUrl); // 及时释放内存

整个过程,用户看到的就是:点按钮 → 按钮变「⏳ 打包中…」→ 几秒后浏览器下载弹窗出现。没有跳转,没有新标签页,干净利落。

注意:Firefox对<a download>有同源限制,但因为我们是用blob:协议创建的URL,完全绕开了这个限制,100%可靠。

4. 实操过程与核心环节实现:从安装到下载的完整 walkthrough

4.1 安装准备:Firefox + Tampermonkey,两步到位

Step 1:确认Firefox版本
必须是Firefox 91或更高版本。低于此版本,img.decode() API不支持,脚本会降级为只抓src,丢失大量懒加载图。检查方法:在地址栏输入about:support,看“版本”一栏。如果不是最新版,先去https://www.mozilla.org/firefox/download/下载安装。别用ESR版,它更新慢,API滞后。

Step 2:安装Tampermonkey
- 打开Firefox附加组件商店:地址栏输入about:addons → 左侧点“扩展” → 右上角搜索框输入“Tampermonkey” → 找到官方作者“Jan Biniok”发布的那个(图标是猴子头)→ 点“添加到Firefox”。
- 安装后,地址栏右侧会出现一个灰色猴子图标。点击它,首次会弹出欢迎页,点“跳过”即可。此时Tampermonkey已就绪。

提示:不要装“Violentmonkey”或“Greasemonkey 4”,虽然它们也能跑,但Greasy Fork的订阅链接是专为Tampermonkey优化的,兼容性最佳。而且Tampermonkey的UI更直观,更新提示更及时。

4.2 安装脚本:两种方式,推荐Greasy Fork一键订阅

方式一:Greasy Fork一键订阅(推荐)
1. 打开脚本主页:https://greasyfork.org/zh-CN/scripts/482321-fantia%E5%9B%BE%E7%89%87%E4%B8%80%E9%94%AE%E4%B8%8B%E8%BD%BD(请以实际发布链接为准)。
2. 页面右上角,绿色大按钮「安装此脚本」。点它。
3. Firefox会弹出Tampermonkey的确认窗口,显示脚本名称、作者、权限请求(主要是“访问fantia.jp”)。仔细看权限描述,确认无敏感请求(它只要求读取fantia.jp的页面,不请求其他域名),点“安装”。
4. 安装成功后,猴子图标会变橙色,并显示小红点。刷新任意Fantia页面(如https://fantia.jp/posts/12345678),按钮应立刻出现。

方式二:手动导入(适合开发者或离线环境)
1. 去GitHub仓库下载ZIP包(KO6zQDc4J0LD46rzff3k-master-8ce262014455072e9ea03149d73444754835d1b7.zip)。
2. 解压,找到FantiaDownloader.js文件。
3. 点击Firefox右上角猴子图标 → “管理面板” → 左侧“用户脚本” → 右上角“创建新脚本”。
4. 删除编辑器里默认的模板代码,把FantiaDownloader.js的全部内容粘贴进去。
5. 按Ctrl+S(Win)或Cmd+S(Mac)保存。脚本立即启用。

注意:手动导入时,务必检查脚本开头的@match元指令是否正确。标准版应为:
// @match https://fantia.jp/posts/*
// @match https://fantia.jp/fanclubs/*/posts/*
如果你只改了@match而忘了保存,脚本不会在Fantia页面运行。这是新手最常见的安装失败原因。

4.3 使用演示:一次完整的下载流程

我们以一个真实场景为例:假设你要下载Fantia用户@art_nova的第88篇投稿,地址是https://fantia.jp/posts/99887766

Step 1:打开页面,等待按钮出现
- 在Firefox中打开该URL。页面加载时,你会看到猴子图标闪烁一下(Tampermonkey正在注入脚本)。
- 等待3~5秒,页面底部、主内容区结束后,会出现一个醒目的红色按钮:「📥 下载全部(0张)」。此时是0张,因为图片还没加载完。

Step 2:触发图片加载,观察计数变化
- Fantia的图是懒加载的,你需要滚动页面,让图片进入视口。慢慢向下滚,每张图进入屏幕,按钮上的数字就会+1。如果你看到数字停在某个值(比如12)不再增加,说明后面还有图被折叠了。这时,把页面拉到底部,等待几秒,看是否出现“加载更多…”提示并自动追加新图。如果有,继续滚动,计数会继续涨。

Step 3:点击下载,监控进度
- 当计数达到你想要的数量(比如25张),点击按钮。按钮立刻变为「⏳ 打包中…(25/25)」,并禁用。
- 打开Firefox开发者工具(F12)→ 切到“控制台”(Console)标签页。你会看到实时日志:
✅ 获取成功: https://cdn.fantia.jp/posts/99887766/attachments/01.jpg
⚠️ 获取失败: https://cdn.fantia.jp/posts/99887766/attachments/05.jpg (TypeError: NetworkError when attempting to fetch resource)
这些日志告诉你哪些图成功了,哪些失败了,原因是什么(网络错误、404、还是CORS?但Firefox下CORS几乎不会发生)。

Step 4:接收ZIP,验证内容
- 通常5~15秒后(取决于图的大小和数量),Firefox下载栏会弹出fantia-1712345678.zip。点击它,或去Ctrl+J打开下载历史,找到它。
- 解压到任意文件夹。打开文件夹,你应该看到:
- 01.jpg, 02.jpg, …, 25.jpg(按URL路径提取的序号命名)
- 或者 cover.jpg, page_1.jpg, comment_01.png(如果URL里有语义化路径)
- 用图片查看器随机打开几张,对比网页上看到的,确认分辨率、色彩、细节完全一致。没有模糊,没有色偏,没有缺角。

实操心得:我测试过单次下载42张图(总大小1.2GB),Firefox内存峰值到1.8GB,但没崩溃。如果你的电脑内存小于8GB,建议单次控制在20张以内,体验更流畅。另外,下载前关闭其他占用内存的标签页(尤其是YouTube、Discord),给Firefox腾出空间。

4.4 高级技巧:应对特殊布局与自定义需求

技巧1:强制刷新图片列表(应对动态加载失效)
有时滚动到底部,新图没加载出来。这时,可以按F5刷新页面,但会丢失已加载的图。更好的办法是:在控制台(F12 → Console)里粘贴并回车:

window.dispatchEvent(new Event('scroll', {bubbles: true}));

这条命令会模拟一次滚动事件,触发Fantia的懒加载钩子,强制它检查并加载视口内的新图。比刷新页面快得多。

技巧2:自定义文件名规则(修改脚本)
默认文件名是从URL路径提取的,但你想改成投稿ID_序号.jpg?打开Tampermonkey管理面板 → 找到本脚本 → 点“编辑” → 搜索function generateFileName(url)。默认实现是:

function generateFileName(url) {
  const path = new URL(url).pathname;
  return path.split('/').pop().replace(/\?.*$/, '') || `image_${Date.now()}`;
}

你可以改成:

function generateFileName(url, index) {
  const postId = location.pathname.match(/\/posts\/(\d+)/)?.[1] || 'unknown';
  return `${postId}_${String(index+1).padStart(3,'0')}.jpg`;
}

保存后,下次下载的文件名就是99887766_001.jpg99887766_002.jpg……非常规整。

技巧3:跳过特定图片(比如水印图)
有些创作者会在图上加半透明水印,URL里带watermark字样。你不想下这些图?在脚本编辑器里,找到图片URL收集那段(搜索const urls = []),在urls.push(cleanUrl)之前加一行:

if (cleanUrl.includes('watermark')) continue; // 跳过含watermark的图

保存,刷新页面,这些图就不会出现在下载列表里了。

5. 常见问题与排查技巧实录:按钮不显示?ZIP为空?图少了?

5.1 问题速查表:症状、原因、解决方案

症状 最可能原因 快速解决方案
按钮完全不出现 ① Tampermonkey未启用;② 脚本未安装或禁用;③ 当前页面URL不匹配@match规则 ① 点猴子图标,确认开关是蓝色;② 管理面板里看脚本状态是否为“启用”;③ 检查地址栏URL是否以https://fantia.jp/posts//fanclubs/开头,不是/login/search
按钮显示「下载全部(0张)」且不变化 ① 页面未加载完;② Fantia新版用了新容器类名;③ 浏览器启用了严格的防跟踪(Enhanced Tracking Protection) ① 等待10秒,手动滚动一下;② 打开控制台,输入document.querySelector("article.post-content")看是否返回null,若是,需更新脚本;③ 地址栏左侧盾牌图标点开,临时关闭ETP
点击按钮后无反应,控制台报错JSZip is not defined JSZip库加载失败(网络问题或CDN被墙) 打开脚本编辑器,找到@require行,把https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js换成https://unpkg.com/jszip@3.10.1/dist/jszip.min.js,保存重试
ZIP包里只有1张图,或全是image_*.jpg 图片URL提取逻辑失效,data-srcsrcset未被捕获 打开控制台,输入document.querySelectorAll("img[data-src]").length,看是否大于0。若为0,说明页面结构变了,需等脚本更新
ZIP解压后图片打不开,显示“文件已损坏” 某些图是WebP格式,Firefox toBlob转JPEG时出错 在脚本编辑器里,找到toBlob那行,把"image/jpeg"改成"image/webp",并把质量1.0降到0.92(WebP高压缩率更稳)

5.2 深度排查:如何读懂控制台日志

当遇到疑难问题,控制台(Console)是你最好的朋友。打开它(F12 → Console),然后:

  • 看红色错误(Error):这是硬性崩溃,比如ReferenceError: JSZip is not defined,说明库没加载。复制错误全文,去GitHub Issues搜,大概率别人已提过。

  • 看黄色警告(Warning):比如[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated...,这是浏览器提醒你脚本里用了过时API,但不影响功能,可忽略。

  • 看绿色成功日志(✅)和黄色失败日志(⚠️):这是脚本自己打印的。重点看失败日志里的URL和错误类型:

  • NetworkError:网络问题,重试即可;
  • TypeError: Failed to fetch:URL无效,可能是404,或Fantia已删除该图;
  • SecurityError:极罕见,说明Firefox的安全策略临时收紧,重启浏览器通常解决。

实操心得:我踩过最大的坑是“按钮在投稿页有效,但在粉丝俱乐部首页无效”。排查发现,俱乐部首页的图是用<amp-img>(AMP组件)渲染的,<img>选择器根本抓不到。解决方案不是硬改选择器,而是加一行:document.querySelectorAll("amp-img, img")。这个教训告诉我:永远假设页面结构会变,脚本要有弹性,而不是指望它永远完美

5.3 版本更新与社区协作:如何获取最新修复

脚本不是一锤子买卖。Fantia每周都可能微调前端,我们的脚本也需持续跟进。更新渠道有两个:

  • 自动更新(Greasy Fork):如果你是一键订阅安装的,Tampermonkey默认开启自动检查。每隔几小时,它会静默比对Greasy Fork上的最新版本号,如有更新,猴子图标会变红,并在管理面板里显示“更新可用”。点击“更新”即可。

  • 手动更新(GitHub):如果你是手动导入的,需要定期去GitHub仓库看Releases页。每次发布,我会写清楚:

  • ✨ 新增:支持了<amp-img>、增加了文件名自定义API;
  • 🐞 修复:解决了srcset解析错误、修正了Firefox 120+的canvas兼容性;
  • 📉 移除:弃用了已下线的CDN,改用更稳定的镜像。

然后,复制新版本的FantiaDownloader.js内容,粘贴到你的本地脚本编辑器里,保存即可。

最后分享一个小技巧:如果你发现某个页面脚本失效了,别急着发issue。先打开控制台,输入document.body.outerHTML.substring(0,500),复制前500字符,连同你的Firefox版本、失效URL,一起贴到GitHub Issue里。这样我能10分钟内复现问题,而不是花半天猜你的环境。开源协作的效率,就藏在这些细节里。

我在实际使用中发现,最可靠的下载体验,永远建立在“理解工具边界”的基础上。它不是万能的,它只承诺:在Firefox里,把你此刻眼睛看到的、浏览器已经加载好的那些图,原封不动地打包给你。不承诺绕过付费墙,不承诺下载私密内容,不承诺处理已被删除的链接。它只是一个诚实的搬运工,而这份诚实,恰恰是它能在无数同类工具中活下来的原因。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:在Fantia网页上自动识别并抓取当前页面所有可见图片,点击新增的‘下载全部’按钮即可生成ZIP压缩包直接保存到本地。整个过程完全在浏览器端运行,不经过任何服务器中转,确保原始图片分辨率、文件名和加载路径不丢失。适配Fantia常见的图文布局,包括单页多图展示、无限滚动分页加载等场景。必须配合Tampermonkey扩展使用,推荐Firefox浏览器——Chrome及Edge等Chromium内核浏览器因CORS跨域限制,可能无法加载部分图片导致打包失败,这不是脚本问题而是浏览器安全策略所致。安装方式简单:从Greasy Fork平台一键订阅,或手动导入FantiaDownloader.js脚本文件。资源包内含完整说明文档README.md,涵盖安装步骤、使用演示、常见报错原因(如按钮不显示、ZIP为空、部分图片缺失)及对应解决方法。源码开源,可随时查看更新日志与社区反馈。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐