京东新品下载

发表于 视频

// ==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();

})();

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。