您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Cleverly download all images on a webpage
当前为
// ==UserScript== // @name Ripper // @namespace http://tampermonkey.net/ // @version 0.1 // @description Cleverly download all images on a webpage // @author TetteDev // @match *://*/* // @icon https://icons.duckduckgo.com/ip2/tampermonkey.net.ico // @license MIT // @grant GM_cookie // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_deleteValue // @grant GM_setValue // @run-at document-idle // @noframes // ==/UserScript== const RenderGui = () => { const guiId = "downloader_gui"; let container = document.getElementById(guiId); if (container) { const _input = container.querySelector('input[type="text"]'); _input.value = ""; _input.dispatchEvent(new Event('input', { 'bubbles': true })); container.remove(); container = document.createElement('div'); } else { container = document.createElement('div'); } container.id = guiId; container.style.cssText = 'position: fixed; top: 10px; right: 10px; background: white; padding: 10px; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.2); z-index: 9999;'; const input = document.createElement('input'); input.type = 'text'; input.placeholder = 'Enter CSS Selector'; input.style.cssText = 'margin-bottom: 10px; padding: 5px; width: 200px; color: black;'; const button = document.createElement('button'); button.textContent = 'Download 0 Image(s)'; button.style.cssText = 'display: block; margin: 10px 0; padding: 5px 10px; cursor: pointer;'; button.onclick = () => { if (previousElements.length < 1) return; const ResolveImageUrl = (img) => { const lazyAttributes = [ "data-src", "data-pagespeed-lazy-src", "srcset", "src", "zoomfile", "file", "original", "load-src", "_src", "imgsrc", "real_src", "src2", "origin-src", "data-lazyload", "data-lazyload-src", "data-lazy-load-src", "data-ks-lazyload", "data-ks-lazyload-custom", "loading", "data-defer-src", "data-actualsrc", "data-cover", "data-original", "data-thumb", "data-imageurl", "data-placeholder", ]; const IsUrl = (url) => { // TODO: needs support for relative file paths also? var pattern = new RegExp( '^(https?:\\/\\/)?'+ // protocol '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string '(\\#[-a-z\\d_]*)?$','i'); let isUrl = !!pattern.test(url); if (!isUrl) { try { new URL(url); return true; } catch(err) { return false; } } return true; }; let possibleImageUrls = lazyAttributes.filter(attr => { let attributeValue = img.getAttribute(attr); if (!attributeValue) return false; attributeValue = attributeValue.replaceAll('\t', '').replaceAll('\n',''); let ok = IsUrl(attributeValue.trim()); if (!ok && attr === "srcset") { // srcset usually contains a comma delimited string that is formatted like // <URL1>, <WIDTH>w, <URL2>, <WIDTH>w, <URL3>, <WIDTH>w, // TODO: handle this case const srcsetItems = attributeValue.split(',').map(attr => attr.trim()).map(item => item.split(' ')); if (srcsetItems.length > 0) { img.setAttribute("srcset", srcsetItems[srcsetItems.length - 1][0]); ok = IsUrl(img.getAttribute("srcset")); } } return ok; }).map(validAttr => img.getAttribute(validAttr).trim()); if (!possibleImageUrls || possibleImageUrls.length < 1) { if (img.hasAttribute("src")) return img.src.trim(); console.error(`Could not resolve the image source URL from the image object`, img); return ""; } return possibleImageUrls.length > 1 ? [...new Set(possibleImageUrls)][0] : possibleImageUrls[0]; }; const urls = previousElements.map(el => { const cb = Array.from(document.querySelectorAll('input[type="checkbox"][id="download_selected"]')).find(cb => typeof cb.for !== "undefined" && cb.for.isEqualNode(el)); if (!cb) debugger; if (!cb.checked) return ""; return ResolveImageUrl(el); }).filter(url => url.length > 0); if (urls.length < 1) return; Download(urls, button); }; const checkbox = document.createElement('input'); checkbox.id = "inherit_cookies"; checkbox.type = 'checkbox'; checkbox.checked = true; const label = document.createElement('label'); label.style.cssText = "color: black;"; label.appendChild(checkbox); label.appendChild(document.createTextNode(' Inherit Session Cookies')); const checkbox_sleep = document.createElement('input'); checkbox_sleep.id = "use_sleep"; checkbox_sleep.type = 'checkbox'; checkbox_sleep.checked = true; const label_sleep = document.createElement('label'); label_sleep.style.cssText = "color: black;"; label_sleep.appendChild(checkbox_sleep); label_sleep.appendChild(document.createTextNode(' Delay between downloads')); const highlightCss = '2px dashed green'; const excludedHighlightCss = '2px dashed gray'; const restoreElements = () => { previousElements.forEach(el => { el.style.border = ''; const cb = Array.from(document.querySelectorAll('input[type="checkbox"][id="download_selected"]')).find(cb => typeof cb.for !== "undefined" && cb.for.isEqualNode(el)); if (cb) cb.remove(); }); previousElements = []; }; let previousElements = []; input.addEventListener('input', () => { restoreElements(); try { const elements = document.querySelectorAll(input.value); button.textContent = `Download ${elements.length} Image(s)`;} catch (e) {} }); input.addEventListener("keyup", (evt) => { if (evt.key !== "Enter") return; restoreElements(); try { const elements = document.querySelectorAll(input.value); elements.forEach(el => { el.style.border = highlightCss; const cb = document.createElement("input"); cb.type = "checkbox"; cb.checked = true; cb.id = "download_selected"; cb.for = el; cb.addEventListener("change", (evt) => { const checked = evt.currentTarget.checked; cb.for.style.border = checked ? highlightCss : excludedHighlightCss; const tmp = Array.from(document.querySelectorAll(input.value)).filter(el => { const cb = Array.from(document.querySelectorAll('input[type="checkbox"][id="download_selected"]')).find(cb => typeof cb.for !== "undefined" && cb.for.isEqualNode(el)); return cb && cb.checked; }); button.textContent = `Download ${tmp.length} Image(s)`; }); el.insertAdjacentElement("beforebegin", cb); previousElements.push(el); }); button.textContent = `Download ${elements.length} Image(s)`; } catch (e) { button.textContent = 'Download 0 Image(s)'; } }); container.appendChild(input); container.appendChild(button); container.appendChild(label); container.appendChild(label_sleep); document.body.appendChild(container); }; const GetBlob = (url, inheritHttpOnlyCookies = true) => { return new Promise(async (resolve, reject) => { const res = await GM.xmlHttpRequest({ method: "GET", url: url, headers: { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Accept-Language": "en-US,en;q=0.9", "Accept-Encoding": "gzip, deflate, br, zstd", "DNT": `${window.navigator.doNotTrack || "1"}`, "Referer": document.location.href || url, "Origin": document.location.origin || url, "Host": window.location.host || window.location.hostname, "User-Agent": window.navigator.userAgent, "Priority": "u=0, i", "Upgrade-Insecure-Requests": "1", "Connection": "keep-alive", //"Cache-Control": "no-cache", "Cache-Control": "max-age=0", "Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-User": "?1", "Sec-GPC": "1", }, responseType: "blob", cookiePartition: { topLevelSite: inheritHttpOnlyCookies ? location.origin : null } }) .catch((error) => { debugger; return reject(error); }); const allowedImageTypes = ["webp","png","jpg","jpeg","gif","bmp"]; const HTTP_OK_CODE = 200; const ok = res.readyState == res["DONE"] && res.status === HTTP_OK_CODE && //res.response && ["webp","image"].some(t => res.response.type.includes(t)) res.response && (res.response.type.startsWith('image/') && allowedImageTypes.includes(res.response.type.split('/')[1].toLowerCase())); if (!ok) { debugger; return reject(error); } return resolve(res.response); }); }; const SaveBlob = async (blob, fileName) => { const MakeAndClickATagAsync = async (blobUrl, fileName) => { try { let link; // Reuse existing element for sequential downloads if (!window._downloadLink) { window._downloadLink = document.createElement("a"); window._downloadLink.style.cssText = "display: none !important;"; try { document.body.appendChild(window._downloadLink); } catch (err) { // Handle Trusted Types policy if (window.trustedTypes && window.trustedTypes.createPolicy) { const policy = window.trustedTypes.createPolicy('default', { createHTML: (string) => string }); } document.body.appendChild(window._downloadLink); } } link = window._downloadLink; // Set attributes and trigger download link.href = blobUrl; link.download = fileName; await Promise.resolve(link.click()); return true; } catch (error) { console.error('Download failed:', error); await Promise.reject([false, error]); } }; const blobUrl = window.URL.createObjectURL(blob) await MakeAndClickATagAsync(blobUrl, fileName) .catch(([state, errorMessage]) => { window.URL.revokeObjectURL(blobUrl); console.error(errorMessage); debugger; return reject([false, errorMessage, res]); }); window.URL.revokeObjectURL(blobUrl); }; const Download = async (LinksArray, downloadButton) => { if (LinksArray.length < 1) return; const shouldInheritCookies = document.getElementById("inherit_cookies")?.checked ?? true; const shouldSleep = document.getElementById("use_sleep")?.checked ?? true; const SleepRange = (min, max) => { const ms = Math.floor(Math.random() * (max - min + 1) + min); return new Promise(r => setTimeout(r, ms)); }; const originalButtonText = downloadButton.textContent; let errored = false; for (let i = 0; i < LinksArray.length; i++) { const url = LinksArray[i]; const fileName = url.split('/').pop().trim(); const blob = await GetBlob(url, shouldInheritCookies).catch(err => { console.error(err); errored = true; }); if (errored) { debugger; break; } await SaveBlob(blob, fileName).catch(err => { debugger; }); if (shouldSleep) await SleepRange(550, 750); downloadButton.textContent = `Downloaded ${i + 1} Image(s) ...`; } if (errored) { alert("Something went wrong with the download, check the developer console!"); } downloadButton.textContent = originalButtonText; }; if (GM_getValue(document.location.host, false)) { RenderGui(); GM_registerMenuCommand("Dont always show GUI for current Host", (evt) => { GM_deleteValue(document.location.host); }); } else { GM_registerMenuCommand("Show GUI", (evt) => { RenderGui(); }); GM_registerMenuCommand("Always show GUI for current Host", (evt) => { GM_setValue(document.location.host, true); RenderGui(); }); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址