news 2026/9/26 20:32:53

115网盘一键转存脚本实战:从原理到避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 模拟点击路线。这个更「笨」但更稳,因为它不做任何网络请求,只是像真人一样在页面上找按钮:

// 路线B:DOM点击型,找转存按钮,模拟用户操作 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/?cid=123456,那 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 现象:遇到「can't 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 面板看真实的转存请求长什么样,照着重放一遍,脚本就能救活。这行做久了你会发现,所谓「一键转存」的稳定性,靠的从来不是魔法,而是把验证步骤做进日常流程的笨功夫。希望这个方向和这些踩坑记录能帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 20:32:43

校园管理系统源码部署与二次开发实战指南

简介&#xff1a;这是一套面向计算机专业本科生的校园管理系统毕业设计/课程设计源码&#xff0c;适用于高校或中小学信息化管理场景&#xff0c;帮助学生快速完成教学实践类项目开发。资源包含219个文件&#xff0c;以43个JSP页面实现前端交互、32个Java类与32个Class文件构成…

作者头像 李华
网站建设 2026/9/26 20:31:23

外星人AWCC彻底卸载重装与启动失败排查指南

1. 外星人智控中心 AWCC 到底是个什么东西 1.1 先搞清楚 AWCC 的定位与核心功能 Alienware Command Center&#xff0c;圈内人一般直接叫 AWCC&#xff0c;是外星人系列笔记本和台式机上的一个系统级控制软件。它不是一个简单的驱动&#xff0c;也不是一个可有可无的装饰性工具…

作者头像 李华
网站建设 2026/9/26 20:29:46

eNSP PRO部署实战:从环境准备到首个拓扑跑通

1. 为什么eNSP PRO值得折腾&#xff1a;从老版eNSP的痛点说起如果你在国内网络工程圈待过几年&#xff0c;大概率绕不开华为eNSP这个模拟器。老版eNSP陪伴了无数人考HCIA、HCIP、HCIE&#xff0c;但它的问题也很明显&#xff1a;只支持Windows、依赖VirtualBox、设备镜像老旧、…

作者头像 李华
网站建设 2026/9/26 20:28:12

专知智库白皮书:用思维操作系统破解信息过载与决策难题

这几年做商业咨询和内部管理&#xff0c;我最大的感受是&#xff1a;大多数人的决策问题&#xff0c;根本不是信息不够&#xff0c;而是处理信息的系统不够。打开手机&#xff0c;行业报告、专家观点、竞品动态、内部数据&#xff0c;全部涌过来&#xff0c;好像什么都看到了&a…

作者头像 李华
网站建设 2026/9/26 20:25:23

泛癌种视角下的KRAS G12C突变分析与蛋白降解剂研发

如果只看单个癌种的测序报告&#xff0c;你可能会觉得KRAS G12C也就是肺腺癌里的一个小分型&#xff0c;量不大&#xff0c;事不多。可一旦把镜头拉到泛癌种维度&#xff0c;情况就完全不是这样——结直肠、胰腺、胆管、甚至部分妇科肿瘤都能见到携带者&#xff1b;更关键的是&…

作者头像 李华
网站建设 2026/9/26 20:25:14

WinForm内嵌ECharts实现实时数据可视化:从交互桥到封装实践

简介&#xff1a;面向.NET桌面开发者的WinForm与ECharts集成示例&#xff0c;核心解决桌面应用中动态数据可视化及前后端交互问题。项目演示通过WebBrowser控件加载HTML&#xff0c;借助InvokeScript将C#侧的新数据推送到ECharts并执行setOption&#xff0c;同时监听ECharts点击…

作者头像 李华