Greasy Fork is available in English.

强大图片提取和批量下载工具

适用于大多数网站的图片提取和批量下载工具,轻松抓取精准限制、无法直接保存的图片

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         强大图片提取和批量下载工具
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  适用于大多数网站的图片提取和批量下载工具,轻松抓取精准限制、无法直接保存的图片
// @author       shenfangda
// @match        *://*/*
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @connect      *
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 配置
    const config = {
        // 默认设置
        defaultSettings: {
            autoFindLarger: true,
            minWidth: 200,
            minHeight: 200,
            maxSize: 100, // MB
            formatFilter: ['jpg', 'jpeg', 'png', 'gif', 'webp'],
            qualityThreshold: 0.5,
            customRules: []
        },
        // 特殊网站规则
        siteRules: {
            '588ku.com': { // 千库网
                selector: 'img[src*="588ku.com"]',
                exclude: ['.icon', '.logo'],
                replacePattern: [/!.*,/g, '']
            },
            'ibaotu.com': { // 包图网
                selector: 'img[src*="ibaotu.com"]',
                exclude: ['.icon', '.logo'],
                replacePattern: [/!.*,/g, '']
            },
            'doc88.com': { // 道客巴巴
                selector: 'img[src*="doc88.com"]',
                exclude: ['.icon', '.logo'],
                replacePattern: [/_thumb\.jpg/g, '.jpg']
            },
            'docin.com': { // 豆丁网
                selector: 'img[src*="docin.com"]',
                exclude: ['.icon', '.logo'],
                replacePattern: [/_thumb\.jpg/g, '.jpg']
            }
        }
    };

    // 主要功能类
    class ImageExtractor {
        constructor() {
            this.images = [];
            this.settings = {...config.defaultSettings};
            this.init();
        }

        init() {
            console.log('图片提取工具已启动');
            this.createUI();
            this.bindEvents();
        }

        // 创建用户界面
        createUI() {
            GM_addStyle(`
                #image-extractor-panel {
                    position: fixed;
                    top: 20px;
                    right: 20px;
                    width: 350px;
                    max-height: 80vh;
                    background: #fff;
                    border: 1px solid #ccc;
                    border-radius: 5px;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                    z-index: 10000;
                    font-family: Arial, sans-serif;
                    font-size: 14px;
                    overflow: hidden;
                    display: none;
                }
                
                #image-extractor-panel-header {
                    background: #4a90e2;
                    color: white;
                    padding: 10px 15px;
                    cursor: move;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                }
                
                #image-extractor-panel-title {
                    font-weight: bold;
                    font-size: 16px;
                }
                
                #image-extractor-panel-close {
                    background: none;
                    border: none;
                    color: white;
                    font-size: 20px;
                    cursor: pointer;
                }
                
                #image-extractor-panel-content {
                    padding: 15px;
                    overflow-y: auto;
                    max-height: calc(80vh - 50px);
                }
                
                .image-extractor-section {
                    margin-bottom: 15px;
                }
                
                .image-extractor-section-title {
                    font-weight: bold;
                    margin-bottom: 8px;
                    color: #333;
                    border-bottom: 1px solid #eee;
                    padding-bottom: 4px;
                }
                
                #image-extractor-images-container {
                    display: grid;
                    grid-template-columns: repeat(3, 1fr);
                    gap: 10px;
                    margin-top: 10px;
                }
                
                .image-extractor-image-item {
                    position: relative;
                    border: 1px solid #ddd;
                    border-radius: 4px;
                    overflow: hidden;
                    cursor: pointer;
                }
                
                .image-extractor-image-item img {
                    width: 100%;
                    height: 80px;
                    object-fit: cover;
                    display: block;
                }
                
                .image-extractor-image-item.selected {
                    border-color: #4a90e2;
                    box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.3);
                }
                
                .image-extractor-image-info {
                    padding: 5px;
                    font-size: 12px;
                    background: rgba(0,0,0,0.7);
                    color: white;
                    position: absolute;
                    bottom: 0;
                    left: 0;
                    right: 0;
                }
                
                .image-extractor-controls {
                    display: flex;
                    flex-wrap: wrap;
                    gap: 8px;
                    margin-top: 10px;
                }
                
                .image-extractor-btn {
                    padding: 8px 12px;
                    background: #4a90e2;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 13px;
                    flex: 1;
                    min-width: 100px;
                }
                
                .image-extractor-btn:hover {
                    background: #357ae8;
                }
                
                .image-extractor-btn:disabled {
                    background: #ccc;
                    cursor: not-allowed;
                }
                
                .image-extractor-btn.secondary {
                    background: #f0f0f0;
                    color: #333;
                }
                
                .image-extractor-btn.secondary:hover {
                    background: #e0e0e0;
                }
                
                .image-extractor-progress {
                    height: 6px;
                    background: #f0f0f0;
                    border-radius: 3px;
                    margin: 10px 0;
                    overflow: hidden;
                    display: none;
                }
                
                .image-extractor-progress-bar {
                    height: 100%;
                    background: #4a90e2;
                    width: 0%;
                    transition: width 0.3s;
                }
                
                .image-extractor-stats {
                    font-size: 13px;
                    color: #666;
                    margin: 10px 0;
                }
                
                .image-extractor-settings {
                    background: #f9f9f9;
                    padding: 10px;
                    border-radius: 4px;
                    margin-top: 10px;
                }
                
                .image-extractor-setting-item {
                    margin-bottom: 8px;
                    display: flex;
                    align-items: center;
                }
                
                .image-extractor-setting-item label {
                    flex: 1;
                    font-size: 13px;
                }
                
                .image-extractor-setting-item input {
                    width: 60px;
                }
                
                #image-extractor-toggle-btn {
                    position: fixed;
                    top: 20px;
                    right: 20px;
                    width: 40px;
                    height: 40px;
                    background: #4a90e2;
                    color: white;
                    border: none;
                    border-radius: 50%;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.2);
                    cursor: pointer;
                    z-index: 9999;
                    font-size: 20px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }
                
                #image-extractor-notification {
                    position: fixed;
                    top: 20px;
                    right: 70px;
                    background: #4a90e2;
                    color: white;
                    padding: 10px 15px;
                    border-radius: 4px;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.2);
                    z-index: 10001;
                    display: none;
                }
            `);

            // 创建主面板
            const panel = document.createElement('div');
            panel.id = 'image-extractor-panel';
            
            panel.innerHTML = `
                <div id="image-extractor-panel-header">
                    <div id="image-extractor-panel-title">图片提取工具</div>
                    <button id="image-extractor-panel-close">×</button>
                </div>
                <div id="image-extractor-panel-content">
                    <div class="image-extractor-section">
                        <div class="image-extractor-section-title">操作</div>
                        <div class="image-extractor-controls">
                            <button id="image-extractor-scan-btn" class="image-extractor-btn">扫描图片</button>
                            <button id="image-extractor-select-all-btn" class="image-extractor-btn secondary">全选</button>
                            <button id="image-extractor-deselect-all-btn" class="image-extractor-btn secondary">取消</button>
                        </div>
                    </div>
                    
                    <div class="image-extractor-section">
                        <div class="image-extractor-section-title">统计信息</div>
                        <div id="image-extractor-stats" class="image-extractor-stats">
                            未扫描图片
                        </div>
                    </div>
                    
                    <div class="image-extractor-section">
                        <div class="image-extractor-section-title">图片预览</div>
                        <div id="image-extractor-images-container">
                            <div style="grid-column: 1 / -1; text-align: center; padding: 20px; color: #999;">
                                点击"扫描图片"开始查找页面中的图片
                            </div>
                        </div>
                    </div>
                    
                    <div class="image-extractor-section">
                        <div class="image-extractor-section-title">下载选项</div>
                        <div class="image-extractor-controls">
                            <button id="image-extractor-download-selected-btn" class="image-extractor-btn" disabled>下载选中</button>
                            <button id="image-extractor-download-zip-btn" class="image-extractor-btn" disabled>打包下载</button>
                        </div>
                    </div>
                    
                    <div class="image-extractor-progress">
                        <div class="image-extractor-progress-bar"></div>
                    </div>
                    
                    <div class="image-extractor-section">
                        <div class="image-extractor-section-title">设置</div>
                        <div class="image-extractor-settings">
                            <div class="image-extractor-setting-item">
                                <label>最小宽度:</label>
                                <input type="number" id="setting-min-width" value="${this.settings.minWidth}" min="50">
                            </div>
                            <div class="image-extractor-setting-item">
                                <label>最小高度:</label>
                                <input type="number" id="setting-min-height" value="${this.settings.minHeight}" min="50">
                            </div>
                            <div class="image-extractor-setting-item">
                                <label>自动查找大图:</label>
                                <input type="checkbox" id="setting-auto-find" ${this.settings.autoFindLarger ? 'checked' : ''}>
                            </div>
                        </div>
                    </div>
                </div>
            `;
            
            document.body.appendChild(panel);
            
            // 创建切换按钮
            const toggleBtn = document.createElement('button');
            toggleBtn.id = 'image-extractor-toggle-btn';
            toggleBtn.innerHTML = '🖼️';
            document.body.appendChild(toggleBtn);
            
            // 创建通知元素
            const notification = document.createElement('div');
            notification.id = 'image-extractor-notification';
            document.body.appendChild(notification);
        }

        // 绑定事件
        bindEvents() {
            // 切换面板显示
            document.getElementById('image-extractor-toggle-btn').addEventListener('click', () => {
                const panel = document.getElementById('image-extractor-panel');
                panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
            });
            
            // 关闭面板
            document.getElementById('image-extractor-panel-close').addEventListener('click', () => {
                document.getElementById('image-extractor-panel').style.display = 'none';
            });
            
            // 拖拽面板
            this.makeDraggable(document.getElementById('image-extractor-panel-header'), document.getElementById('image-extractor-panel'));
            
            // 扫描图片
            document.getElementById('image-extractor-scan-btn').addEventListener('click', () => {
                this.scanImages();
            });
            
            // 全选
            document.getElementById('image-extractor-select-all-btn').addEventListener('click', () => {
                this.selectAllImages();
            });
            
            // 取消全选
            document.getElementById('image-extractor-deselect-all-btn').addEventListener('click', () => {
                this.deselectAllImages();
            });
            
            // 下载选中
            document.getElementById('image-extractor-download-selected-btn').addEventListener('click', () => {
                this.downloadSelectedImages();
            });
            
            // 打包下载
            document.getElementById('image-extractor-download-zip-btn').addEventListener('click', () => {
                this.downloadAsZip();
            });
            
            // 设置变更
            document.getElementById('setting-min-width').addEventListener('change', (e) => {
                this.settings.minWidth = parseInt(e.target.value) || 200;
            });
            
            document.getElementById('setting-min-height').addEventListener('change', (e) => {
                this.settings.minHeight = parseInt(e.target.value) || 200;
            });
            
            document.getElementById('setting-auto-find').addEventListener('change', (e) => {
                this.settings.autoFindLarger = e.target.checked;
            });
        }

        // 使面板可拖拽
        makeDraggable(header, panel) {
            let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
            
            header.onmousedown = dragMouseDown;
            
            function dragMouseDown(e) {
                e = e || window.event;
                e.preventDefault();
                // 获取鼠标位置
                pos3 = e.clientX;
                pos4 = e.clientY;
                document.onmouseup = closeDragElement;
                document.onmousemove = elementDrag;
            }
            
            function elementDrag(e) {
                e = e || window.event;
                e.preventDefault();
                // 计算新位置
                pos1 = pos3 - e.clientX;
                pos2 = pos4 - e.clientY;
                pos3 = e.clientX;
                pos4 = e.clientY;
                // 设置元素新位置
                panel.style.top = (panel.offsetTop - pos2) + "px";
                panel.style.left = (panel.offsetLeft - pos1) + "px";
            }
            
            function closeDragElement() {
                // 停止移动
                document.onmouseup = null;
                document.onmousemove = null;
            }
        }

        // 扫描页面中的图片
        scanImages() {
            this.showNotification('正在扫描图片...');
            this.images = [];
            
            // 获取当前网站规则
            const hostname = window.location.hostname;
            let rule = null;
            for (const site in config.siteRules) {
                if (hostname.includes(site)) {
                    rule = config.siteRules[site];
                    break;
                }
            }
            
            // 查找所有图片元素
            const imgElements = document.querySelectorAll('img');
            const bgImages = this.extractBackgroundImages();
            const canvasImages = this.extractCanvasImages();
            
            // 合并所有图片
            const allImages = [...imgElements, ...bgImages, ...canvasImages];
            
            // 处理图片
            allImages.forEach((img, index) => {
                try {
                    let src = '';
                    let width = 0;
                    let height = 0;
                    
                    if (img.tagName === 'IMG') {
                        src = img.src || img.dataset.src || '';
                        width = img.naturalWidth || img.width || 0;
                        height = img.naturalHeight || img.height || 0;
                    } else if (img.type === 'background') {
                        src = img.src;
                        width = img.width || 0;
                        height = img.height || 0;
                    } else if (img.type === 'canvas') {
                        src = img.src;
                        width = img.width || 0;
                        height = img.height || 0;
                    }
                    
                    // 应用规则处理
                    if (rule && rule.replacePattern) {
                        rule.replacePattern.forEach(pattern => {
                            src = src.replace(pattern, '');
                        });
                    }
                    
                    // 过滤条件
                    if (!src || src.startsWith('data:')) return;
                    if (width < this.settings.minWidth || height < this.settings.minHeight) return;
                    
                    this.images.push({
                        id: index,
                        src: src,
                        width: width,
                        height: height,
                        element: img,
                        selected: false
                    });
                } catch (e) {
                    console.warn('处理图片时出错:', e);
                }
            });
            
            // 去重
            const uniqueImages = [];
            const seenUrls = new Set();
            
            this.images.forEach(img => {
                if (!seenUrls.has(img.src)) {
                    seenUrls.add(img.src);
                    uniqueImages.push(img);
                }
            });
            
            this.images = uniqueImages;
            
            // 更新UI
            this.updateImageList();
            this.updateStats();
            this.showNotification(`找到 ${this.images.length} 张图片`);
        }

        // 提取背景图片
        extractBackgroundImages() {
            const bgImages = [];
            const elements = document.querySelectorAll('*');
            
            elements.forEach(el => {
                const computedStyle = window.getComputedStyle(el);
                const bgImage = computedStyle.backgroundImage;
                
                if (bgImage && bgImage !== 'none') {
                    // 提取URL
                    const urlMatch = bgImage.match(/url\(["']?(.*?)["']?\)/);
                    if (urlMatch && urlMatch[1]) {
                        const img = new Image();
                        img.src = urlMatch[1];
                        img.type = 'background';
                        
                        // 获取元素尺寸作为图片尺寸
                        const rect = el.getBoundingClientRect();
                        img.width = rect.width;
                        img.height = rect.height;
                        
                        bgImages.push(img);
                    }
                }
            });
            
            return bgImages;
        }

        // 提取Canvas图片
        extractCanvasImages() {
            const canvasImages = [];
            const canvases = document.querySelectorAll('canvas');
            
            canvases.forEach(canvas => {
                try {
                    const dataURL = canvas.toDataURL('image/png');
                    const img = new Image();
                    img.src = dataURL;
                    img.type = 'canvas';
                    
                    // 获取Canvas尺寸
                    img.width = canvas.width;
                    img.height = canvas.height;
                    
                    canvasImages.push(img);
                } catch (e) {
                    // 跨域Canvas无法提取
                    console.warn('无法提取Canvas图片:', e);
                }
            });
            
            return canvasImages;
        }

        // 更新图片列表
        updateImageList() {
            const container = document.getElementById('image-extractor-images-container');
            container.innerHTML = '';
            
            if (this.images.length === 0) {
                container.innerHTML = '<div style="grid-column: 1 / -1; text-align: center; padding: 20px; color: #999;">未找到符合条件的图片</div>';
                return;
            }
            
            this.images.forEach(img => {
                const item = document.createElement('div');
                item.className = 'image-extractor-image-item';
                item.dataset.id = img.id;
                
                item.innerHTML = `
                    <img src="${img.src}" onerror="this.parentElement.innerHTML='<div style=\'width:100%;height:80px;display:flex;align-items:center;justify-content:center;color:#999;font-size:12px;\'>图片加载失败</div>'">
                    <div class="image-extractor-image-info">${img.width}×${img.height}</div>
                `;
                
                if (img.selected) {
                    item.classList.add('selected');
                }
                
                item.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.toggleImageSelection(img.id);
                });
                
                container.appendChild(item);
            });
            
            // 更新按钮状态
            document.getElementById('image-extractor-download-selected-btn').disabled = this.images.filter(i => i.selected).length === 0;
            document.getElementById('image-extractor-download-zip-btn').disabled = this.images.length === 0;
        }

        // 切换图片选择状态
        toggleImageSelection(id) {
            const img = this.images.find(i => i.id === id);
            if (img) {
                img.selected = !img.selected;
                const item = document.querySelector(`.image-extractor-image-item[data-id="${id}"]`);
                if (item) {
                    if (img.selected) {
                        item.classList.add('selected');
                    } else {
                        item.classList.remove('selected');
                    }
                }
                
                // 更新按钮状态
                document.getElementById('image-extractor-download-selected-btn').disabled = this.images.filter(i => i.selected).length === 0;
            }
        }

        // 全选图片
        selectAllImages() {
            this.images.forEach(img => {
                img.selected = true;
            });
            this.updateImageList();
        }

        // 取消全选图片
        deselectAllImages() {
            this.images.forEach(img => {
                img.selected = false;
            });
            this.updateImageList();
        }

        // 更新统计信息
        updateStats() {
            const selectedCount = this.images.filter(i => i.selected).length;
            const stats = document.getElementById('image-extractor-stats');
            stats.textContent = `共找到 ${this.images.length} 张图片,已选择 ${selectedCount} 张`;
        }

        // 下载选中图片
        async downloadSelectedImages() {
            const selectedImages = this.images.filter(i => i.selected);
            if (selectedImages.length === 0) {
                this.showNotification('请先选择要下载的图片');
                return;
            }
            
            this.showProgress(0);
            
            for (let i = 0; i < selectedImages.length; i++) {
                const img = selectedImages[i];
                try {
                    await this.downloadImage(img.src, `image_${i + 1}.jpg`);
                    this.showProgress(((i + 1) / selectedImages.length) * 100);
                } catch (e) {
                    console.error('下载图片失败:', e);
                }
            }
            
            this.hideProgress();
            this.showNotification(`下载完成,共下载 ${selectedImages.length} 张图片`);
        }

        // 打包下载为ZIP
        async downloadAsZip() {
            if (this.images.length === 0) {
                this.showNotification('没有图片可以下载');
                return;
            }
            
            this.showProgress(0);
            
            try {
                const zip = new JSZip();
                const imgFolder = zip.folder("images");
                const selectedImages = this.images.filter(i => i.selected).length > 0 ? 
                                      this.images.filter(i => i.selected) : this.images;
                
                for (let i = 0; i < selectedImages.length; i++) {
                    const img = selectedImages[i];
                    try {
                        const blob = await this.fetchImageAsBlob(img.src);
                        const extension = this.getImageExtension(img.src);
                        imgFolder.file(`image_${i + 1}.${extension}`, blob);
                        this.showProgress(((i + 1) / selectedImages.length) * 100);
                    } catch (e) {
                        console.error('添加图片到ZIP失败:', e);
                    }
                }
                
                const content = await zip.generateAsync({type: "blob"});
                saveAs(content, `images_${new Date().getTime()}.zip`);
                
                this.hideProgress();
                this.showNotification(`ZIP打包完成,共包含 ${selectedImages.length} 张图片`);
            } catch (e) {
                this.hideProgress();
                this.showNotification('打包下载失败: ' + e.message);
                console.error('ZIP打包失败:', e);
            }
        }

        // 下载单张图片
        downloadImage(url, filename) {
            return new Promise((resolve, reject) => {
                GM_download({
                    url: url,
                    name: filename,
                    onload: () => resolve(),
                    onerror: (error) => reject(error)
                });
            });
        }

        // 获取图片Blob
        fetchImageAsBlob(url) {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: url,
                    responseType: "blob",
                    onload: function(response) {
                        resolve(response.response);
                    },
                    onerror: function(error) {
                        reject(error);
                    }
                });
            });
        }

        // 获取图片扩展名
        getImageExtension(url) {
            const match = url.match(/\.([^.]+)(\?.*)?$/);
            return match ? match[1].toLowerCase() : 'jpg';
        }

        // 显示进度条
        showProgress(percent) {
            const progress = document.querySelector('.image-extractor-progress');
            const bar = document.querySelector('.image-extractor-progress-bar');
            progress.style.display = 'block';
            bar.style.width = percent + '%';
        }

        // 隐藏进度条
        hideProgress() {
            const progress = document.querySelector('.image-extractor-progress');
            progress.style.display = 'none';
        }

        // 显示通知
        showNotification(message) {
            const notification = document.getElementById('image-extractor-notification');
            notification.textContent = message;
            notification.style.display = 'block';
            
            setTimeout(() => {
                notification.style.display = 'none';
            }, 3000);
        }
    }

    // 初始化插件
    new ImageExtractor();
})();