编辑代码

// ==UserScript==
// @name         一键批量下载淘宝图片
// @namespace    https://item.taobao.com
// @version	1.0.4
// @description	 一键批量下载淘宝主图,sku以及详情页(重构和经过了一些逻辑上的改良,按钮更协调;内存占用更小;操作更流畅)
// @author       Leo
// @homepage
// @match        https://item.taobao.com/*
// @match        https://item.taobao.hk/*
// @grant        GM_log
// @grant        GM_setClipboard
// @grant        GM_notification
// @grant        GM_download
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant			   GM_addStyle
// ==/UserScript==
/*jshint multistr:true */

/*
 * 下载天猫图片工具
 * 下载商品主图
 * 下载sku
 * 下载详情页
 * */

window.onload = () => {
    insertJQuery('https://stuk.github.io/jszip/vendor/FileSaver.js')
    insertJQuery('https://stuk.github.io/jszip/dist/jszip.js')
    insertJQuery('https://stuk.github.io/jszip-utils/dist/jszip-utils.js')

    const props = getData()
    setStyle()
    getProductImage(props)
    getProductInfoPage(props)
}

function insertJQuery(url) {
    let script = document.createElement('script');
    script.src = url;
    document.head.appendChild(script);
    return script;
}


// 获取数据方法
function getData() {
    // 全部变量
    const props = {}
    const params = location.search.substr(1).split('&')
    for (let i in params) {
        props[params[i].split('=')[0]] = unescape(params[i].split('=')[1])
    }
    return props
}

/**
 * 获取网络图片content
 * @param {Srting}url
 * @return {Promise<unknown>}
 */
function getPicture(url) {
    return new Promise((resolve, reject) => {
        fetch(url, {
            method: 'get',
            responseType: 'arraybuffer'
        }).then(data => {
            return data.blob()
        }).then(data => {
            resolve(data)
        }).catch(error => {
            reject(error)
        })
    })
}

function download(images, folderName) {
    const zip = new JSZip()
    const folder = zip.folder(folderName)
    const promises = images.map(image => {
        return getPicture(image).then(data => {
            // 获取图片名
            const name = image.split('/')[image.split('/').length - 1]
            folder.file(name, data, { binary: true })
        })
    })
    Promise.all(promises).then(() => {
        zip.generateAsync({ type: 'blob' }).then(content => {
            saveAs(content, folderName + '.zip')
        })
    })

}

// 注入下载商品主图方法
function getProductImage(props) {
    // 获取主图缩略图列表DOM
    let imgList = document.getElementById('J_UlThumb')
    // 创建并注入下载专区
    const downBox = document.createElement('div')
    document.getElementsByClassName('tb-gallery')[0].appendChild(downBox)
    // 创建并注入标题
    const title = document.createElement('h2')
    title.innerHTML = '下载主图'
    title.style = 'text-align: center; margin-bottom: 20px; margin-top: 20px'
    downBox.appendChild(title)
    // 创建并插入下载按钮列表
    const downList = document.createElement('ul')
    downList.className = 'tb-thumb tb-clearfix thumb-ul'
    downBox.appendChild(downList)

    // 创建主图的下载链接列表
    const imgArr = []

    // 遍历主图缩略图,并插入下载按钮
    for (let k = 0; k < imgList.getElementsByTagName('li').length; k++) {
        // 获取主图下载链接
        let src

        const xpath = document.evaluate(
            `//li[${k + 1}]/div/a/img`,
            imgList,
            null,
            XPathResult.ANY_TYPE,
            null
        )
        let img = xpath.iterateNext()

        if (k === 0) {
            // 检查首图视频
            const arr = imgList
                .getElementsByTagName('li')[0]
                ?.getElementsByTagName('span')
			if (!(arr ?.length >= 1)) {
                src = img ?.src
			}
        } else {
            src = img ?.src
		}

        if (src) {
            src = src.substr(0, src.length - 16)
            // 注入给下载列表
            imgArr.push(src)
            // 创建并注入按钮
            const downButton = document.createElement('li')
            downButton.style =
                'display:flex; flexflow: row; align-items: center; justify-content: center'
            const downImg = document.createElement('img')
            downImg.src = src
            downImg.style.cursor = 'pointer'
            downImg.title = (k + 1).toString()
            downImg.onmouseover = () => {
                imgList.getElementsByTagName('li')[k].className = 'tb-selected'
            }
            downImg.onmouseout = () => {
                imgList.getElementsByTagName('li')[k].className = 'tb-thumb tm-clear'
            }
            downImg.onclick = () => {
                GM_download(src, `${props.id}_主图_${k >= 9 ? k + 1 : '0' + (k + 1)}`)
            }
            downList.appendChild(downButton)
            downButton.appendChild(downImg)
        }
    }

    // 注入下载全部按钮
    const downAll = document.createElement('li')
    downAll.style =
        'display:flex; flexflow: row; align-items: center; justify-content: center'
    const downAllSpan = document.createElement('a')
    downAllSpan.style.cursor = 'pointer'
    downAllSpan.onclick = () => {
        GM_notification('共下载 ' + imgArr.length + ' 张图片', '下载主图')
        download(imgArr, '主图')
		/*imgArr.forEach((i, k) => {
			GM_download(
				i,
				`${props.id}_主图_${k >= 9 ? k + 1 : '0' + (k + 1)}${i.substr(-4, 4)}`
			)
		})*/
    }
    downAllSpan.innerHTML = '全部'
    downList.appendChild(downAll)
    downAll.appendChild(downAllSpan)

    return imgArr
}

// 注入下载详情页方法
function getProductInfoPage(props) {
    console.log('创建详情页图片列表***')
    // 创建详情页图片列表
    const imgArr = []

    // 获取表单
    const editForm = document.getElementById('J_TabBar')
    // 创建下载按钮
    const downButton = document.createElement('li')
    downButton.style =
        'display:flex; flexflow: row; align-items: center; justify-content: center'
    const downA = document.createElement('a')
    // 调整属性
    downA.innerHTML = '下载详情页'
    downA.style = 'color: red; font-weight: bold;'
    downA.title =
        '请注意,下载详情图之前请先将所有详情图片显示完毕再点击此按钮下载,否则会出现下载不完整等问题'
    downA.onclick = () => {
        downButton.className = 'tm-selected'
        const imgList = document.getElementsByClassName('ke-post')[0].childNodes
        // todo 改成多层遍历,穿透获取
        // 分层穿透,
        // 检查节点是否含有src
        // 检查节点是否有子元素 ———闭包遍历
        const imgArr = []
        const mapChild = list => {
            list.forEach(i => {
                if (i ?.src) {
                    imgArr.push(i.src)
                }
                if (i.childNodes.length !== 0) {
                    mapChild(i.childNodes)
                }
            })
        }
        mapChild(imgList)
        GM_notification('共下载 ' + imgArr.length + ' 张图片', '下载详情页')
        // 下载图片
        download(imgArr, '详情页')
		/*for (let k = 0; k < imgArr.length; k++) {
			GM_download(
				imgArr[k],
				`${props.id}_详情页_${k >= 9 ? k + 1 : '0' + (k + 1)}`
			)
		}*/
    }
    // 将下载按钮注入表单
    editForm.insertBefore(
        downButton,
        document.getElementsByClassName('tm-qrcode-icon')[0]
    )
    downButton.appendChild(downA)
}

// 注入样式
function setStyle() {
    GM_addStyle(`
.thumb-ul{
		font-family:Arial;
		font-weight:bold;
	}
	.thumb-ul li{
		border-style:solid;
		border-color:#FE4403!important;
		font-family:Arial;
		font-weight:bold;
		font-size:16px;
		cursor:pointer;
	}
	.cat-ul li{
		cursor:pointer;
		font-size:14px;
		font-family:Arial;
	}
	.detail-li,.border-li{
		width:40px!important;
		padding:0px!important;
	}
	.tb-tabbar>li{
		min-width:80px!important;
	}
`)
}