|
我也写了js版的,手机浏览器也能用。部署到CloudFlare Worker就行。
代码如下:
export default { async fetch(request, env, ctx) { const url = new URL(request.url);
// 1. 处理代理请求 (绕过跨域并添加 Rust 代码中指定的请求头) if (url.pathname === '/proxy') { const targetUrl = url.searchParams.get('url'); if (!targetUrl) return new Response('Missing URL', { status: 400 });
const proxyRequest = new Request(targetUrl, { method: 'GET', headers: { 'User-Agent': 'Mozilla/5.0', 'Cookie': 'ismob=0' } });
try { const response = await fetch(proxyRequest); // 复制响应并加上 CORS 头,允许前端读取数据 const newResponse = new Response(response.body, response); newResponse.headers.set('Access-Control-Allow-Origin', '*'); return newResponse; } catch (e) { return new Response(e.message, { status: 500 }); } }
// 2. 默认路由返回前端 HTML 页面 const html = ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>网页图片打包下载器</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script> <style> body { font-family: system-ui, sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; line-height: 1.6; } h2 { color: #333; } input[type="text"] { width: 100%; padding: 10px; margin-bottom: 15px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { padding: 10px 20px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:disabled { background-color: #aaa; cursor: not-allowed; } #status { margin-top: 20px; color: #555; font-weight: bold; } </style> </head> <body> <h2>图片下载打包器</h2> <input type="text" id="targetUrl" placeholder="请输入目标网页的 URL"> <button id="downloadBtn">提取并打包</button> <div id="status"></div>
<script> document.getElementById('downloadBtn').addEventListener('click', async () => { const urlInput = document.getElementById('targetUrl').value.trim(); const statusEl = document.getElementById('status'); const btn = document.getElementById('downloadBtn'); if (!urlInput) { alert('请输入URL!'); return; }
btn.disabled = true; try { // 第一步:通过代理获取网页 HTML statusEl.innerText = '正在获取网页内容...'; const htmlRes = await fetch('/proxy?url=' + encodeURIComponent(urlInput)); const htmlText = await htmlRes.text();
// 第二步:解析 HTML,寻找特定的 img 标签 const parser = new DOMParser(); const doc = parser.parseFromString(htmlText, 'text/html'); // 选择器和逻辑:查找 img 并获取 ess-data 属性 const images = doc.querySelectorAll('img[ess-data]');
if (images.length === 0) { statusEl.innerText = '未在页面中找到带有 ess-data 属性的图片。'; btn.disabled = false; return; }
const zip = new JSZip(); let count = 0; // 第三步:循环下载图片并加入 ZIP for (let i = 0; i < images.length; i++) { let imgUrl = images.getAttribute('ess-data'); // 处理相对路径问题,确保是完整 URL if (imgUrl.startsWith('//')) { imgUrl = 'https:' + imgUrl; } else if (imgUrl.startsWith('/')) { const baseUrl = new URL(urlInput); imgUrl = baseUrl.origin + imgUrl; }
statusEl.innerText = \`正在下载图片 \${i + 1} / \${images.length} ...\`; // 通过代理获取图片二进制数据 const imgRes = await fetch('/proxy?url=' + encodeURIComponent(imgUrl)); const blob = await imgRes.blob(); // 命名逻辑:{count}.jpg zip.file(\`\${count}.jpg\`, blob); count++; }
// 第四步:生成 ZIP 并触发浏览器下载 statusEl.innerText = '正在生成 ZIP 文件,请稍候...'; const zipBlob = await zip.generateAsync({type: 'blob'}); const downloadLink = document.createElement('a'); downloadLink.href = URL.createObjectURL(zipBlob); downloadLink.download = 'images.zip'; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); URL.revokeObjectURL(downloadLink.href);
statusEl.innerText = \`完成!共成功打包了 \${count} 张图片。\`;
} catch (e) { statusEl.innerText = '发生错误: ' + e.message; } finally { btn.disabled = false; } }); </script> </body> </html> `;
return new Response(html, { headers: { 'Content-Type': 'text/html;charset=UTF-8' } }); } }
|