// ==UserScript==
// @name 京东SKU高清图下载器-美化面板版
// @namespace http://tampermonkey.net/
// @version 2.0
// @description 京东商品流提取商品标题和高清图,按SKU文件夹打包下载
// @match *://*.jd.com/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @grant GM_xmlhttpRequest
// @connect m.360buyimg.com
// @connect img*.360buyimg.com
// ==/UserScript==
(function () {
'use strict';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
function cleanImageUrl(url) {
if (!url) return "";
url = url.replace(/s\d+x\d+_/g, "");
url = url.replace(/!q\d+\.webp/g, "");
url = url.replace(/!.*$/g, "");
return url;
}
function safeName(name) {
return name
.replace(/[\\/:*?"<>|]/g, "_")
.replace(/\s+/g, " ")
.trim()
.slice(0, 80);
}
async function scrollOnce() {
window.scrollBy({
top: window.innerHeight * 0.9,
behavior: "smooth"
});
setStatus("已下滑一次");
await sleep(900);
}
async function scrollTimes(times) {
times = Number(times) || 1;
for (let i = 1; i <= times; i++) {
setStatus(`正在下滑 ${i}/${times}`);
window.scrollBy({
top: window.innerHeight * 0.9,
behavior: "smooth"
});
await sleep(1000);
}
setStatus(`下滑完成,共 ${times} 次`);
}
function collectProducts() {
const cards = document.querySelectorAll(".feeds-card");
const products = [];
const seen = new Set();
cards.forEach((card, index) => {
const titleDom = card.querySelector(".title-text");
const imgDom = card.querySelector(".card-img img, img");
if (!titleDom || !imgDom) return;
const title = titleDom.innerText.trim();
let imgUrl = imgDom.currentSrc || imgDom.src || imgDom.getAttribute("src");
imgUrl = cleanImageUrl(imgUrl);
if (!title || !imgUrl) return;
if (seen.has(imgUrl)) return;
seen.add(imgUrl);
const fileName = imgUrl.split("/").pop().split(".")[0];
const sku = safeName(fileName || `sku_${index + 1}`);
products.push({
sku,
title,
imgUrl
});
});
return products;
}
function fetchBlob(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url,
responseType: "blob",
headers: {
"Referer": location.href,
"User-Agent": navigator.userAgent
},
onload(res) {
if (res.status >= 200 && res.status < 300) {
resolve(res.response);
} else {
reject(new Error("状态码:" + res.status));
}
},
onerror(err) {
reject(err);
}
});
});
}
async function downloadZip() {
const products = collectProducts();
if (!products.length) {
setStatus("未提取到商品,请先下滑加载商品");
return;
}
const zip = new JSZip();
setStatus(`已提取 ${products.length} 个商品,开始下载图片`);
for (let i = 0; i < products.length; i++) {
const item = products[i];
setStatus(`下载中 ${i + 1}/${products.length}`);
const folder = zip.folder(item.sku);
folder.file("skutitle.txt", item.title);
try {
const blob = await fetchBlob(item.imgUrl);
let ext = ".jpg";
if (item.imgUrl.includes(".png")) ext = ".png";
if (item.imgUrl.includes(".webp")) ext = ".webp";
folder.file(item.sku + ext, blob);
} catch (e) {
folder.file("download_failed_url.txt", item.imgUrl);
console.log("下载失败:", item.imgUrl, e);
}
await sleep(200);
}
setStatus("正在打包 ZIP");
const content = await zip.generateAsync({
type: "blob"
});
saveAs(content, "京东SKU高清图.zip");
setStatus(`完成:${products.length} 个商品`);
}
function setStatus(text) {
const el = document.querySelector("#jd-sku-status");
if (el) el.innerText = text;
}
function createPanel() {
const panel = document.createElement("div");
panel.innerHTML = `
<div id="jd-sku-panel">
<div class="jd-sku-title">京东SKU高清图下载</div>
<div class="jd-sku-row">
<input id="jd-scroll-times" type="number" min="1" value="10">
<button id="jd-scroll-times-btn">按次数下滑</button>
</div>
<button class="jd-main-btn" id="jd-scroll-once-btn">下滑一次</button>
<button class="jd-download-btn" id="jd-download-btn">提取并下载</button>
<div id="jd-sku-status">等待操作</div>
</div>
`;
document.body.appendChild(panel);
const style = document.createElement("style");
style.innerHTML = `
#jd-sku-panel {
position: fixed;
right: 24px;
top: 120px;
width: 230px;
z-index: 999999;
background: rgba(255,255,255,0.96);
border-radius: 18px;
box-shadow: 0 10px 35px rgba(0,0,0,0.18);
padding: 16px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
color: #222;
backdrop-filter: blur(8px);
border: 1px solid rgba(0,0,0,0.06);
}
.jd-sku-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 14px;
text-align: center;
color: #e1251b;
}
.jd-sku-row {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
#jd-scroll-times {
width: 70px;
height: 36px;
border-radius: 10px;
border: 1px solid #ddd;
padding: 0 8px;
font-size: 14px;
outline: none;
}
#jd-scroll-times-btn,
.jd-main-btn,
.jd-download-btn {
border: none;
cursor: pointer;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
transition: all .2s ease;
}
#jd-scroll-times-btn {
flex: 1;
background: #f3f4f6;
color: #333;
}
.jd-main-btn {
width: 100%;
height: 40px;
margin-bottom: 10px;
background: linear-gradient(135deg, #ff7a45, #ff4d4f);
color: white;
}
.jd-download-btn {
width: 100%;
height: 44px;
background: linear-gradient(135deg, #e1251b, #b00000);
color: white;
font-size: 15px;
}
#jd-scroll-times-btn:hover,
.jd-main-btn:hover,
.jd-download-btn:hover {
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(225,37,27,0.25);
}
#jd-sku-status {
margin-top: 12px;
font-size: 12px;
color: #666;
line-height: 1.5;
text-align: center;
word-break: break-all;
}
`;
document.head.appendChild(style);
document.querySelector("#jd-scroll-once-btn").onclick = scrollOnce;
document.querySelector("#jd-scroll-times-btn").onclick = () => {
const times = document.querySelector("#jd-scroll-times").value;
scrollTimes(times);
};
document.querySelector("#jd-download-btn").onclick = downloadZip;
}
createPanel();
})();
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。