Greasy Fork

Greasy Fork is available in English.

孔夫子旧书网无水印图片下载助手

一键批量下载孔夫子旧书网商品图片(无水印版本)

目前为 2024-12-22 提交的版本,查看 最新版本

// ==UserScript==
// @name         孔夫子旧书网无水印图片下载助手
// @description  一键批量下载孔夫子旧书网商品图片(无水印版本)
// @version      1.0.0
// @author       骄阳哥
// @namespace    jyg
// @match        *://search.kongfz.com/product_result/*
// @match        *://book.kongfz.com/*
// @match        *://item.kongfz.com/book/*
// @grant        GM_addStyle
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 当前页面URL
    const currentUrl = window.location.href;

    // 移除图片水印
    function removeImageWatermark(imgUrl) {
        return imgUrl.replace(/(_water|_n|_p|_b|_s)/g, '');
    }

    // 创建商品详情页下载按钮
    function createDetailPageButton(images) {
        const btn = document.createElement('button');
        btn.innerText = `📥 下载全部图片(${images.length}张)`;
        btn.id = 'kfz-download-btn';
        btn.style.backgroundColor = '#1890ff';
        btn.style.color = 'white';
        document.body.appendChild(btn);
        return btn;
    }

    // 创建搜索页面下载按钮
    function createSearchPageButton(doc, item) {
        const btn = doc.createElement('button');
        btn.innerText = '📥 下载图片';
        btn.className = 'kfz-search-download-btn';
        btn.style.backgroundColor = '#1890ff';
        const cartBtn = item.querySelector('div.add-cart-btn');
        cartBtn.parentNode.insertBefore(btn, cartBtn);
        return btn;
    }

    // 创建书籍列表页下载按钮
    function createListPageButton(doc, item) {
        const btn = doc.createElement('button');
        btn.innerText = '📥 下载图片';
        btn.className = 'kfz-list-download-btn';
        btn.style.backgroundColor = '#1890ff';
        const cartBtn = item.querySelector('a.con-btn-cart');
        cartBtn.parentNode.insertBefore(btn, cartBtn.nextSibling);
        return btn;
    }

    // 获取商品图片列表
    function getBookImages(doc) {
        const imgItems = doc.querySelectorAll('ul#figure-info-box > li');
        return Array.from(imgItems, item => {
            const img = item.querySelector('img');
            const imgSrc = img ? img.getAttribute('_viewsrc') : null;
            return removeImageWatermark(imgSrc);
        });
    }

    // 下载图片
    function downloadImages(doc, btn) {
        const images = getBookImages(doc);
        if(images.length === 0) {
            console.warn('未找到可下载的图片');
            btn.innerText = '😅 暂无可下载的图片';
            btn.style.backgroundColor = '#999';
            btn.disabled = true;
            return;
        }

        console.log(`找到${images.length}张图片待下载:`, images);
        btn.disabled = true;
        btn.innerText = '下载中...';

        let successCount = 0;
        let failCount = 0;

        // 获取书名和ISBN
        const bookName = doc.querySelector('h1')?.innerText || '未知书名';
        const isbnInfo = doc.querySelector('meta[name="description"]').getAttribute('content').match(/ISBN:([0-9]*)/);
        const isbn = isbnInfo?.[1] || '';

        console.log('书籍信息:', {
            bookName,
            isbn
        });

        // 下载每张图片
        images.forEach((imgUrl, index) => {
            if(!imgUrl) {
                console.error(`第${index + 1}张图片URL无效`);
                failCount++;
                return;
            }

            const downloadWithRetry = (url, hasWatermark = false) => {
                const ext = url.split('.').pop()?.toLowerCase() || 'jpg';
                const watermarkText = hasWatermark ? '-有水印' : '';
                const fileName = `${bookName.trim()}-${isbn.trim()}-${index + 1}${watermarkText}.${ext}`;

                console.log(`开始下载第${index + 1}张图片:`, {
                    url,
                    fileName,
                    hasWatermark
                });

                GM_download({
                    url,
                    name: fileName,
                    onload: () => {
                        successCount++;
                        console.log(`第${index + 1}张图片下载成功:`, {
                            url,
                            fileName,
                            successCount,
                            total: images.length
                        });

                        btn.innerText = `下载中...(${successCount}/${images.length})`;

                        if(successCount === images.length) {
                            console.log('所有图片下载完成!');
                            btn.innerText = `✅ ${successCount}张图片已下载`;
                            btn.style.backgroundColor = '#52c41a';
                        }
                    },
                    onerror: (err) => {
                        if(!hasWatermark) {
                            // 无水印版本下载失败,尝试下载带水印版本
                            console.log(`第${index + 1}张无水印图片下载失败,尝试下载带水印版本:`, {
                                url,
                                error: err
                            });

                            const watermarkUrl = url.replace(/\.([^.]*)$/, '_b.$1');
                            downloadWithRetry(watermarkUrl, true);
                        } else {
                            // 带水印版本也下载失败
                            failCount++;
                            console.error(`第${index + 1}张图片下载失败(带水印版本):`, {
                                url,
                                error: err,
                                failCount,
                                total: images.length
                            });

                            btn.innerText = `❌ ${failCount}张图片下载失败`;
                            if(err.error) {
                                console.error('错误详情:', err.error);
                            }
                        }
                    }
                });
            };

            // 开始下载无水印版本
            downloadWithRetry(imgUrl);
        });
    }

    // 从URL获取并下载图片
    function downloadFromUrl(url, btn) {
        btn.addEventListener('click', () => {
            console.log('开始获取页面:', url);

            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                onload: response => {
                    console.log('页面获取成功:', {
                        url,
                        status: response.status
                    });

                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');
                    downloadImages(doc, btn);
                },
                onerror: err => {
                    console.error('页面获取失败:', {
                        url,
                        error: err
                    });
                    btn.innerText = '❌ 获取图片失败';
                }
            });
        });
    }

    // 处理搜索页面
    function handleSearchPage(item) {
        const link = item.querySelector('.item-info > .title > a');
        const btn = createSearchPageButton(document, item);
        downloadFromUrl(link.href, btn);
    }

    // 处理列表页面
    function handleListPage(item) {
        const link = item.querySelector('div.list-con-title > a');
        const btn = createListPageButton(document, item);
        downloadFromUrl(link.href, btn);
    }

    // 初始化页面
    let checkInterval;

    if(currentUrl.includes('book.kongfz.com')) {
        const btn = createDetailPageButton(getBookImages(document));
        btn.addEventListener('click', () => downloadImages(document, btn));
    }
    else if(currentUrl.includes('search.kongfz.com/product_result')) {
        checkInterval = setInterval(() => {
            const listBox = document.querySelector('#listBox');
            if(listBox) {
                clearInterval(checkInterval);
                document.querySelectorAll('#listBox .item')
                    .forEach(item => handleSearchPage(item));
            }
        }, 1000);
    }
    else if(currentUrl.includes('item.kongfz.com/book')) {
        checkInterval = setInterval(() => {
            const listBox = document.querySelector('ul.itemList');
            if(listBox) {
                clearInterval(checkInterval);
                document.querySelectorAll('ul.itemList > li')
                    .forEach(item => handleListPage(item));
            }
        }, 1000);
    }

    // 注入样式
    GM_addStyle(`
        #kfz-download-btn {
            position: fixed;
            bottom: 30px;
            right: 30px;
            padding: 12px 24px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
            transition: all 0.3s;
            z-index: 9999;
        }

        #kfz-download-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
        }

        .kfz-search-download-btn,
        .kfz-list-download-btn {
            padding: 4px 12px;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
            margin: 0 8px;
            transition: all 0.3s;
        }

        .kfz-search-download-btn:hover,
        .kfz-list-download-btn:hover {
            opacity: 0.8;
        }

        button:disabled {
            background-color: #999 !important;
            cursor: not-allowed;
            opacity: 0.7;
        }
    `);

})();