115网盘一键转存脚本实战:从原理到避坑指南
简介115一键转存.user.js 是一款面向115网盘用户的效率辅助脚本基于油猴Tampermonkey等浏览器扩展运行能够在115网盘页面中直接触发显著简化批量转存、批量创建共享链接以及获取下载链接等高频操作适合从个人资料整理到团队文件协作分享等不同使用场景。压缩包为rar格式文件总数仅1个核心为js脚本资源包整体约9KB体积小巧便于存储与分发也方便在多个设备间迁移使用。该脚本已吸引11430人学习下载关注度较高侧面反映其在115用户中的实用价值与稳定性口碑。功能上它支持一键为多个文件或文件夹生成共享链接、快速提取下载地址并能批量处理大量文件避免逐一手动操作脚本还包含定时转存、新上传文件自动处理等自动化逻辑可降低日常网盘维护成本让文件管理更省心。脚本使用中应关注来源可靠性遵循115网盘服务条款并随平台更新及时维护版本确保功能正常与账号安全。1. 115一键转存脚本是什么从分享链接到入库的最后一公里你肯定遇到过这种情况群里有人丢过来一个115分享链接里面躺着一整个剧集文件夹或者几十本电子书你点进去看看想存到自己网盘里结果发现要么文件太散、要么子文件夹套娃手动一个一个勾选再转存没个十分钟下不来。所谓「115一键转存脚本」本质上就是跑在浏览器里的用户脚本UserScript所以文件名里常带 .user它替你把「打开分享页 → 勾选所有文件 → 选择保存目录 → 点击转存」这一串重复动作自动做完。它不是破解、不是盗链只是把你在网页上本来就会做的点击操作用脚本在页面上下文里模拟执行一遍。我用这类脚本有三四年早期从「手动点 50 个文件」到「一条脚本跑完」的体验差距是质的飞跃。适合谁资源整理重度用户、从旧网盘往 115 迁资料的搬运党、以及看到分享链接就手痒想囤货的人。它解决的核心问题就一个把「手动点鼠标」变成「一脚本跑完」同时把每一步都落在可见的网页反馈上出了问题你能知道它死在哪儿。这个方向值得投入因为它不依赖任何后门只依赖你对网页接口和 DOM 结构的理解。2. 转存脚本的两种实现路线网页接口调用与 DOM 模拟点击2.1 为什么选择油猴脚本而非独立爬虫写转存工具第一反应是写个 Python 爬虫模拟登录然后调 API。这个思路在 115 上行不通原因是登录态和风控。115 网页端的 Cookie 里有加密签名还绑定了设备指纹你用 requests 把 Cookie 塞进去第一次可能成功第二次就给你返回需要验证的提示。血泪经验告诉我独立爬虫最大的坑不是接口参数而是你无法稳定维持一个「看起来像真人」的会话环境。油猴脚本Tampermonkey / Violentmonkey跑在浏览器页面里天然继承当前标签页的 Cookie、会话、UA 和 referrer。它就像是你自己在页面上操作风控角度和真实用户几乎无差别。另一个优势是调试方便打开 F12 就能看到脚本报错改完刷新即生效。代价是脚本和页面结构耦合115 前端一改版你的脚本就可能翻车所以后面避坑章节会专门讲怎么应对。还有一条路线是写浏览器插件Extension权限更大但要打包、签名而且浏览器商店审核会卡掉一批和自动化相关的功能。用户脚本没有分发审核问题新建文件粘贴代码就能用所以社区里流传的转存脚本基本都是 .user.js 格式。这也是标题里带 .user 的原因——它不是一个可执行 exe而是需要借助油猴扩展运行的文本脚本。2.2 解析分享链接的字段pickcode、cid、file_id不管走哪条实现路线你都得先弄懂 115 分享页里的几个核心字段否则脚本写出来也跑不通。打开任意一个 115 分享链接按 F12 切到 Network 面板刷新页面找到名为shareinfo或files的 XHR 请求响应 JSON 里通常会有这些字段pickcode分享码也就是链接里那串字母数字转存时的核心凭证。分享链接格式一般是115.com/s/{pickcode}后端靠它定位分享内容。cid目录 ID。每个用户网盘里的文件夹都有一个 cid根目录通常是0。转存时必须指定把文件放进哪个 cid不传就默认存根目录。file_id/fid文件 ID。文件名、大小、父目录都是挂在它下面的批量转存时用它来匹配结果。实际抓包时你会发现 response 里还可能有category_id、user_id之类但前三者是脚本必须读的。注意pickcode是区分分享来源的一个分享链接可能包含多个fid所以脚本要做的是「遍历分享页里的所有 fid打包成一次或多次转存请求」。理解这个数据结构你才知道为什么有的脚本只能转存单文件、有的能存整个文件夹——区别就在于有没有去递归读取子目录。2.3 两种路线的代码骨架对比下面给两个最简骨架一个是拦截接口数据后自行调用转存接口另一个是纯模拟鼠标点击。先看接口调用路线核心是在分享页里执行一段 fetch 或 GM_xmlhttpRequest// 路线A接口调用型从分享页请求里拿到文件列表后直接调转存接口 async function transferByApi(pickcode, targetCid) { // 1. 拿到分享文件列表这里假设你已经从页面请求里读取到 data const fileList await getShareFileList(pickcode); // 需要自己实现的函数 // 2. 构造转存请求体 const form new FormData(); form.append(pickcode, pickcode); form.append(cid, targetCid); form.append(fid_list, JSON.stringify(fileList.map(f f.fid))); // 3. 发请求注意带上当前页面的 Cookie 会自动由浏览器携带 const res await fetch(/web/transfer, { method: POST, body: form, headers: { X-Requested-With: XMLHttpRequest } }); const json await res.json(); console.log(转存结果, json); }这段代码的逻辑是先把分享页的文件列表抽出来然后用浏览器自带 fetch 发 POST。关键参数是pickcode分享码、cid要存到的目标目录、fid_list要转入的文件 ID 数组。好处是速度快、可控性强坏处是接口路径和参数可能随版本变化一旦变了就要重新抓包。再看 DOM 模拟点击路线。这个更「笨」但更稳因为它不做任何网络请求只是像真人一样在页面上找按钮// 路线BDOM点击型找转存按钮模拟用户操作 function transferByClick() { // 等待按钮出现有些页面是异步渲染的 const btn waitForElement(#transferBtn); if (!btn) throw new Error(找不到转存按钮); btn.click(); // 必要时循环点击每个文件前的复选框 document.querySelectorAll(.file-check).forEach(cb cb.click()); // 等待弹窗里的保存到网盘按钮 const confirmBtn waitForElement(.modal .confirm-btn); confirmBtn.click(); }waitForElement是用MutationObserver写的短轮询函数避免元素还没渲染就点击失败。这条路线不需要维护接口字段115 改接口不影响但改 DOM 就影响。我的做法是能用接口调用的场景优先用接口接口失效时退化成 DOM 模拟两条路线同步维护。实战中你不需要二选一而是根据页面改动灵活切换——这一点在后面的避坑章节会有具体案例。3. 把脚本装进浏览器安装、配置与最小可运行流程3.1 油猴扩展安装与脚本新建步骤先确保浏览器里装好了 Tampermonkey 或 Violentmonkey这两个扩展都支持 .user.js 脚本。安装方式不展开说了扩展商店里直接搜「Tampermonkey」注意识别开发者是 Jan Biniok 的正版。装好后浏览器工具栏会出现一个拼图图标点开就是油猴管理面板。新建脚本的入口在油猴面板的「添加新脚本」按钮。点击后会生成一个模板里面有name、match、grant等元信息。一个干净的模板看起来是这样// UserScript // name 115一键转存助手 // namespace local.transfer.115 // version 0.1.0 // description 在115分享页提供一键转存按钮 // match https://115.com/s/* // match https://115.com/* // grant none // /UserScript这里match决定了脚本在哪些网址生效。我一般会同时写上https://115.com/s/*分享页和https://115.com/*主站因为转存完成后可能会跳回网盘。grant none表示不申请额外的 GM API如果你要用 GM_xmlhttpRequest 或 GM_setValue需要改成对应的 grant。新手最容易踩的坑是忘了写match结果脚本在其他页面打开一脸懵。3.2 最小脚本从点击转存按钮到弹出成功提示把模板保存后我们写一个最简可运行脚本在分享页顶部插入一个「一键转存」按钮点击后自动触发网盘自带的转存逻辑。先不追求自动化勾选只验证「脚本能在页面里执行」。(function () { use strict; // 等待页面加载完再操作DOM function waitForPage() { return new Promise(resolve { if (document.readyState complete) return resolve(); window.addEventListener(load, () resolve()); }); } async function main() { await waitForPage(); // 在页面右上角插入一个按钮 const btn document.createElement(button); btn.innerHTML 一键转存; btn.style.position fixed; btn.style.top 80px; btn.style.right 20px; btn.style.zIndex 99999; btn.addEventListener(click, () { // 真正要做的逻辑找到转存按钮并点击 const transferBtn document.querySelector(.btn-transfer); if (transferBtn) { transferBtn.click(); alert(已触发转存请在弹出的窗口中选择目录); } else { alert(没找到转存按钮请检查当前页面是否为分享页); } }); document.body.appendChild(btn); } main(); })();这段代码做的事情很朴素页面加载后插入一个悬浮按钮点击时寻找页面上真实的转存按钮并触发它。逻辑说明就一句话脚本只是替你按鼠标。alert是调试用的后续可以换成日志输出。注意zIndex要设得够高否则可能被其他浮层盖住。跑通这一步你已经完成「脚本能在 115 页面运行」的最小验证。3.3 脚本里必须配置的参数cid、uid与转存目录分享页本身没有目标目录信息所以脚本里通常要让你自己指定一个「送到哪个文件夹」。常见做法是脚本设置面板里加一个输入框让你填 cid。但我更推荐在网盘主站里「右键文件夹 → 复制链接」链接里的数字就是该文件夹的 cid。比如打开网盘里某个文件夹地址栏显示https://115.com/?cid123456那 123456 就是你要填的 cid。用户名和 uid 问题115 转存接口有时会校验当前登录用户身份如果脚本用 GM_xmlhttpRequest 发起跨域请求少带 uid 参数就可能报权限错误。从 Cookie 里的NETU_ID或页面请求响应里能读到 uid脚本里最好用一个常量先占位const config { targetCid: 123456, // 从网盘目录链接里复制 uid: 888888, // 从首页网络请求里找 USER_ID batchSize: 10, // 每批转存文件数 intervalMs: 3000 // 每次转存间隔避免风控 };注意这里的 uid 不是你的登录邮箱而是数字 ID。找不到也没关系现阶段的接口调用型脚本里很多情况下不传 uid 也能成功但如果出现「参数错误」的报错回来检查这个值就行。这几个参数就是你以后调脚本的主要「旋钮」cid 错了东西存错地方uid 错了权限校验失败intervalMs 太短会触发人机验证。我在真实项目里就是这么配的宁可把参数放在脚本顶部集中管理也不要散落在各个函数里。4. 核心逻辑拆解从分享页抓取文件列表到批量转存4.1 抓取分享页文件列表的三种策略第一是监听 XHR 响应。115 的分享页是 SPA文件列表要靠异步请求加载。你在 Network 面板里看到那个返回文件列表的接口后用脚本 hook 住XMLHttpRequest把 responseText 里的file_list提取出来。下面是 hook 代码的核心片段// 策略一hook XMLHttpRequest拿到真实接口的返回 (function () { const originalOpen XMLHttpRequest.prototype.open; const originalSend XMLHttpRequest.prototype.send; window.addEventListener(load, () { // 不在这里动等用户打开分享页 }); XMLHttpRequest.prototype.send function (...args) { this.addEventListener(load, function () { const url this.responseURL || ; if (url.includes(shareinfo)) { try { const data JSON.parse(this.responseText); window.__shareFileList data; // 暂存到全局供脚本使用 } catch (e) {} } }); return originalSend.apply(this, args); }; })();这段代码的原理是在 XHR 的load事件里检查响应 URL如果命中文件列表接口就把完整响应暂存到window.__shareFileList。好处是你不需要自己拼接口地址页面已经帮你请求过坏处是 hook 必须在页面网络请求发生前注入否则只能拿到之后的数据。因此脚本的run-at document-start要加上。第二是主动请求接口。在 Network 面板里找到https://115.com/web/lixian/shareinfo这类地址后脚本直接用 fetch 重放。注意要带上从页面里解析出的pickcode参数async function fetchShareList(pickcode) { const url https://115.com/web/lixian/shareinfo?pickcode${pickcode}; const res await fetch(url, { credentials: include, // 关键带Cookie headers: { X-Requested-With: XMLHttpRequest } }); return res.json(); }credentials: include是必须的没有它请求就是未登录态。这个策略的优点是代码简单、不依赖页面执行顺序缺点是如果接口路径变了你需要回来改 URL。第三是解析 DOM。如果接口彻底变了那就退回到「读页面渲染出来的文件名」遍历分享页里的.file-name元素拼成列表。这个最慢但在任何历史版本都能用。我一般把这个当作兜底方案前两个方案失效时启用。4.2 调用转存接口的请求格式与签名头拿到文件列表后真正的转存请求通常长这样以接口型为例async function doTransfer(pickcode, fidList, targetCid) { const body new URLSearchParams(); body.append(pickcode, pickcode); body.append(cid, targetCid); body.append(fid_list, JSON.stringify(fidList)); const res await fetch(https://115.com/web/transfer, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded, X-Requested-With: XMLHttpRequest, Origin: https://115.com, Referer: location.href }, body: body.toString(), credentials: include }); return res.json(); }请求头里最关键的是Referer。有些接口会校验 Referer如果你的脚本在浏览器的 console 里运行Referer 默认是当前页面没问题但如果用 GM_xmlhttpRequest 发跨域请求手动的 Referer 要填对。另外fid_list是 JSON 字符串不是普通数组拼写错误会导致后端解析失败这是最常见的黑匣子问题。响应里一般会有state: true和data字段如果state为 false先看msg是什么再逐项排查参数。4.3 批量转存的任务队列与限速115 的风控规则虽然不写进文档但实测规律很明显短时间连续转存超过一定数量就会弹人机验证。所以批量转存必须做成队列逐个发送每发一次暂停几秒。下面是一个简单的任务队列实现async function batchTransfer(pickcode, items, targetCid, intervalMs 3000) { const results []; for (let i 0; i items.length; i) { try { const res await doTransfer(pickcode, [items[i]], targetCid); results.push({ item: items[i], state: res.state }); console.log(进度 ${i 1}/${items.length}, res.msg || res.state); } catch (err) { console.error(第 ${i} 个转存失败, err); // 失败不中断继续后面的最后统一看结果 } await sleep(intervalMs); } return results; } function sleep(ms) { return new Promise(resolve setTimeout(resolve, ms)); }注意这里按[items[i]]每次只传一个文件而不是一次传全部。这不是我保守而是亲测教训一次传 50 个文件大概率有一部分会静默失败返回结果里却显示成功。逐文件转存虽然慢但每个文件的结果单独可查失败还能重试。intervalMs我一般设 3000~5000太快容易触发验证太慢让人等得心焦。你可以根据自己账号的情况调整如果连续跑几次都没被验证可以适当缩到 2000。5. 115转存脚本避坑指南5个我踩过的雷5.1 现象脚本提示转存成功但网盘里找不到文件有次我写了个新脚本跑完所有文件都返回state: true满心欢喜去网盘检查目录里空空如也。原因在于 115 的转存接口是「异步任务」模式接口返回true只是表示「接收了你的请求」后台还在排队执行。如果你立刻去目录刷新看不到是正常的。解决在拿到state: true后再发一个mtask查询请求确认任务已完成。任务详情接口需要返回一个task_id轮询这个接口直到文件出现在目录里。代码里可以在doTransfer的响应中取出data.task_id然后每 2 秒查一次最多等 30 秒。后来我把校验写死在批量流程里宁可慢也不假设成功。5.2 现象转存目录固定为根目录无法选择子目录脚本刚开始能转存但每次都存到根目录明明targetCid填了一个子目录的 ID。排查发现接口需要的是cid还是category_id我填的目录链接里取的是cid但那段接口认的其实是user_id下的分类 ID两个不是同一个东西。解决在网盘主站点击目标文件夹看地址栏里cid后面的数字再去 Network 面板里找到该文件夹的详情请求对比响应 JSON 里的cid和folder_id字段。你会发现地址栏的cid可能就是接口要的cid也可能加了一个pid前缀。我的做法是在配置里同时放cid和category_id用哪个取决于当前配置的接口版本不再写死。5.3 现象遇到「cant verify the user is human. please try again.」弹窗这是最常见的风控反馈。触发原因基本是批量转存间隔太短或者频繁刷新分享页。我第一次遇到时以为是账号被标记吓得赶紧停手。实际上只要调整节奏就能恢复。解决把intervalMs从 2000 改成至少 5000并且在脚本里加一个「遇到验证码就自动暂停 5 分钟」的保护逻辑。实现方式是监听alert或检测页面是否出现验证码 iframe一旦出现就停止队列。另外要注意脚本调用接口的频率和页面本身加载资源不同115 会单独统计接口频率所以不仅要在两次转存之间加延时还要避免同时开多个分享页脚本并发跑。5.4 现象批量转存时部分文件丢失没有报错这个最阴险。脚本日志里每个文件都显示成功但入库后数一数少了三个。后来对比发现缺失的文件名里有特殊字符比如「」和「?」这些字符在 115 的某些历史版本里不允许作为文件名接口会静默跳过。还有一种是空文件夹分享列表里有目录但没有子文件转存时不会报错但结果为空。解决转存前先对name字段做一次清洗把非法字符替换成下划线空目录单独标记不进入批量队列。更重要的验证手段是转存完成后调用目录文件列表接口把返回的文件名和原分享列表逐一比对缺哪个就从日志里找出失败原因。这段比对代码我放在最后一章讲因为它是我现在每次跑大量数据前必做的「后悔药」。5.5 现象油猴脚本在115页面失效升级了一次浏览器或 115 页面改版后脚本突然不执行了油猴面板里脚本是开启的但按钮不出现。排查方向先看match是否还命中新版 115 可能把分享页域名从115.com/s/改成了anxia.com/s/或加了二级目录。另外 SPA 页面可能是事后渲染脚本执行时 DOM 还没生成按钮加不进去。解决把match写成通配符比如*://115.com/*和*://*.115.com/*然后给「按钮出现」加一个延时轮询不要依赖load事件因为 SPA 的load早于页面内容渲染。我最后用的兜底是在setInterval里每 500ms 检查一次按钮父元素直到出现才插入最多检查 20 次否则才报「页面结构异常」。6. 验证脚本是否可靠日志校验、双写比对与日常维护给脚本加一个简单的本地日志记录不用引入第三方库就用GM_setValue存到扩展的存储里。每次转存完成后把文件和目标目录的对应关系存下来。这样即使脚本跑完第二天才发现少了文件你还能从日志里回找原因。function logTransfer(item, state, msg) { const log GM_getValue(transferLog, []); log.push({ name: item.name, fid: item.fid, time: Date.now(), state, msg }); GM_setValue(transferLog, log.slice(-200)); // 只保留最后200条 }日志里的state是接口返回的成功标志但实际入库情况要对账。我现在的习惯是跑完脚本后再写一条「双写比对」流程调用目标目录的文件列表把「原分享文件名集合」和「目标目录文件名集合」做差集差集里的就是漏掉或清洗掉的文件。比对脚本只需十几行但它是最后一道闸门每次转存完跑一遍至少能保证心里有数。最后说一个我养成的习惯每次 115 前端大版本更新后先跑一个单文件转存验证脚本是否还能工作然后再跑批量。因为转存脚本的技术栈本身不难难的是跟上页面变化。遇到失效别慌打开 Network 面板看真实的转存请求长什么样照着重放一遍脚本就能救活。这行做久了你会发现所谓「一键转存」的稳定性靠的从来不是魔法而是把验证步骤做进日常流程的笨功夫。希望这个方向和这些踩坑记录能帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →