动态网页文件下载技能(Web File Downloader)
适用于附件链接为
javascript:void(0)的动态加载页面,通过浏览器自动化逆向工程提取真实下载地址。
快速开始
当用户说「帮我把这个页面的 PDF 下载下来」「这个附件打不开」等类似需求时:
- 加载
browser-agent技能 - 按「五步法」流程执行
- 下载文件到本地并验证完整性
- 交付给用户
适用场景
✅ 典型场景
| 场景 | 示例 | |------|------| | 政府公告附件 | 招标公告、中标通知书、政策文件 | | 国企采购平台 | 国网电商平台、中石化采购网等 | | 企业内部系统 | OA 系统文档预览页、知识库附件 | | 招投标网站 | 公共资源交易中心、政府采购网 |
❌ 不适用场景
- 页面上已有明确
.pdf/.doc下载链接 → 直接用 curl 下载 - 需要登录才能访问的文件 → 先引导用户完成登录
- 文件需要付费购买 → 提示用户自行完成支付
- 非公开/受版权保护的文件 → 拒绝并说明原因
核心方法:五步法
总体流程
注入钩子 → 触发请求 → 捕获响应 → 逆向源码 → 拼接下载
前提条件
⚠️ 以下约束来自大量实测经验,违反将导致任务失败。
| 约束项 | 要求 | 原因 |
|--------|------|------|
| 执行环境 | use_main_world: true | 隔离沙箱中钩子无法拦截页面真实请求 |
| 截图 | 禁止 browser_take_screenshot | 截图数据量大会导致上下文 token 溢出熔断 |
| 输出长度 | 每次 evaluate ≤ 2500 字符 | 超长返回会被截断,大 JSON 需分段读取 |
| 重复执行 | 禁止连续执行相同 JS | 会触发重复操作熔断机制 |
| 最大次数 | browser_evaluate ≤ 6 次 | 超过后服务端强制终止 |
第 1 步:注入响应监听钩子
在页面主执行环境注入 XHR 响应拦截器,记录所有接口的完整响应。
// browser_evaluate (use_main_world: true)
(function(){
window.__resp = {};
var os = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(){
var s = this;
s.addEventListener('load', function(){
try {
if (s.__u) {
window.__resp[s.__u] = (s.responseText || '').slice(0, 8000);
}
} catch(e){}
});
return os.apply(this, arguments);
};
if (!XMLHttpRequest.prototype.__oP) {
var oo = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(m, u) {
this.__u = u;
return oo.apply(this, arguments);
};
XMLHttpRequest.prototype.__oP = true;
}
return 'hooked';
})()
要点:
- 必须用
use_main_world: true,否则脚本在浏览器隔离沙箱运行,拦截不到页面真实请求 - 钩子同时拦截
open和send,通过__oP标志防止重复注入 - 响应截取前 8000 字符,覆盖绝大多数 JSON 接口响应
第 2 步:触发文档请求
通过页面内路由切换重新触发 API 请求,确保钩子能捕获到完整的接口调用链。
browser_navigate → 站点首页(如 #/)→ 等待 2 秒
browser_navigate → 原文档页 → 等待 6~8 秒
如果文档需要点击附件才加载,则在导航回文档页后:
browser_snapshot获取页面快照- 在快照中找到含
.pdf文字的附件链接 browser_click点击该链接- 等待 4~5 秒让异步请求完成
第 3 步:读取捕获的接口响应
// browser_evaluate (use_main_world: true)
(function(){
var keys = Object.keys(window.__resp || {});
var out = [];
for (var i = 0; i < keys.length; i++) {
var v = window.__resp[keys[i]];
if (v && v.length > 100) {
out.push(keys[i] + ' => ' + v.slice(0, 2000));
}
}
return out.join('\n---\n').slice(0, 2400) || 'EMPTY';
})()
如果返回 EMPTY:
- 检查钩子是否在导航时被清除(页面刷新会重置 JS 环境)
- 尝试先注入钩子 → 再导航回文档页(调整第 1、2 步顺序)
如果响应过长被截断,分段读取:
// 读取中段
(function(){ var r = window.__resp; var k = Object.keys(r); return k.map(function(x){ return x + ' => ' + r[x].slice(2000, 4000); }).join('\n').slice(0, 2400); })()
// 读取后段
(function(){ var r = window.__resp; var k = Object.keys(r); return k.map(function(x){ return x + ' => ' + r[x].slice(4000, 6000); }).join('\n').slice(0, 2400); })()
从响应中识别关键信息:
| 字段类型 | 常见字段名 | 说明 |
|---------|-----------|------|
| 文件路径 | FILE_PATH, filePath, url, attachUrl, ossUrl | 可能是加密值 |
| 文件 ID | fileId, attachId, docId, id | 用于构造下载请求 |
| 文件名 | FILE_NAME, fileName, name, title | 用于保存文件 |
第 4 步:在前端 JS 源码中找下载接口模板
// browser_evaluate (use_main_world: true)
(async function(){
var out = [];
var srcs = Array.prototype.slice.call(document.querySelectorAll('script[src]'))
.map(function(s){ return s.src; })
.filter(function(u){ return u.indexOf(location.hostname) > -1; });
var kws = ['FILE_PATH', 'downloadFile', 'downLoad', 'getFile',
'pdfjs', 'viewer.html', 'showPDF', 'attachUrl'];
for (var k = 0; k < kws.length && out.length < 6; k++) {
for (var i = 0; i < srcs.length && out.length < 6; i++) {
try {
var t = await (await fetch(srcs[i])).text();
var idx = t.indexOf(kws[k]);
while (idx > -1 && out.length < 6) {
out.push('KW=' + kws[k] + '|' + srcs[i].split('/').pop() + '@' + idx
+ ':' + t.slice(Math.max(0, idx - 150), idx + 250).replace(/\s+/g, ' '));
idx = t.indexOf(kws[k], idx + 1);
}
} catch(e){}
}
}
return out.join('\n=====\n').slice(0, 2300) || 'NOT-FOUND';
})()
从源码片段中识别下载 URL 模板,常见模式:
| 模式 | 示例 |
|------|------|
| window.open 拼接 | window.open(adr + "/index/downLoadWinFile?filePath=" + l.FILE_PATH + "&fileName=" + l.FILE_NAME) |
| PDF 预览器加载 | this.pdfv.load({fileurl: adr + "/index/showPDF?filePath=" + n}) |
| fetch / xhr 请求 | fetch(baseUrl + "/api/attach/download?id=" + fileId) |
| <a> 动态设置 href | link.href = baseUrl + "/files/" + item.path |
第 5 步:拼接 URL 并下载
根据模板拼出完整下载 URL,用 curl 下载:
# URL 编码文件路径参数(Python 方式)
ENC_PATH=$(python -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "$FILE_PATH")
# 下载文件
curl -sk --max-time 300 -L -o "文件名.pdf" \
-H "Referer: https://目标域名/..." \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36" \
"完整下载URL"
下载后验证(必须执行):
# 检查文件头(PDF 应以 %PDF- 开头)
head -c 16 "文件名.pdf" | od -c | head -2
# 检查文件尾(PDF 应以 %%EOF 结尾)
tail -c 64 "文件名.pdf" | od -c | tail -3
# 检查文件大小是否合理
ls -la "文件名.pdf"
已验证站点模板
以下模板经过实测验证,可直接使用。其他站点请按五步法动态分析。
国网电商平台 (ecp.sgcc.com.cn)
| 项目 | 值 |
|------|-----|
| 元数据接口 | /ecp2.0/ecpwcmcore/index/getWinFile |
| 加密路径字段 | FILE_PATH(十六进制加密,含 \| 分隔符) |
| 文件名段 | FILE_NAME |
| 下载接口 | /ecp2.0/ecpwcmcore/index/downLoadWinFile?filePath={FILE_PATH}&fileName={FILE_NAME} |
| 预览接口 | /ecp2.0/ecpwcmcore/index/showPDF?filePath={FILE_PATH} |
| 注意事项 | filePath 需要 URL 编码;需携带 Referer 请求头 |
通用站点分析模板
对于未收录的站点,按以下检查清单逐步分析:
□ 1. 注入钩子后导航触发请求
□ 2. 从捕获的接口中找到返回文件信息的 API
□ 3. 提取文件路径/ID 字段
□ 4. 搜索前端源码找到 URL 拼接模板
□ 5. 构造完整 URL 并下载
□ 6. 验证文件完整性
故障排除
按现象查原因
| 现象 | 可能原因 | 解决方案 |
|------|---------|---------|
| Hook 捕获为空数组 [] | 脚本在隔离沙箱运行 | 所有 browser_evaluate 必须加 use_main_world: true |
| 上下文超限熔断 | 截图或大 JSON 撑爆 token | 禁止 browser_take_screenshot;输出截断到 2500 字符 |
| 重复操作熔断 | 连续执行相同 JS 脚本 | 每次 evaluate 脚本必须有差异 |
| 接口响应被截断 | 单次返回长度限制 | 分段读取:slice(0,2400) → slice(2400,4800) → slice(4800,7200) |
| 前端源码搜索无结果 | JS 文件过多或托管在 CDN | 缩小范围到同域名脚本;增加关键词变体 |
| 下载得到 HTML 而非 PDF | URL 拼接错误或需要认证 | 检查 URL 参数;确认是否需要 Cookie/Token |
| 文件大小为 0 或极小 | 接口返回错误信息 | 检查响应 content-type 和状态码 |
重试策略
- 钩子未捕获 → 调整注入和导航顺序(先钩子后导航)
- 源码搜索无果 → 扩大关键词范围,或直接在 Network 面板中手动检查
- 下载失败 → 增加 Cookie、Token 等认证头,或尝试预览接口
安全与合规
- ✅ 仅用于下载公开可访问的文件(公示、公告、招标文件等)
- ✅ 不绕过登录/权限验证;如需登录则先引导用户完成
- ✅ 下载文件保存在本地,不外传到第三方服务
- ✅ 遵守目标网站的 robots.txt 和使用条款
- ❌ 不用于批量爬取或自动化数据采集
- ❌ 不用于获取受版权保护的非公开内容
版本历史
| 版本 | 日期 | 变更 | |------|------|------| | 2.0.0 | 2026-08-08 | 根据 SkillHub 五维评测优化:增加场景分类、约束表格、分段读取、已验证模板、检查清单、重试策略 | | 1.0.0 | 2026-08-08 | 初版:五步法核心流程、国网平台模板、故障排除 |
微信扫一扫