图片下载器

可以在绝大多数网站提取并批量下载图片。尤其是类似于千库网、包图网这种,不能右键保存图片或者保存的图片文件格式无法识别,均可以用脚本提取,然后用脚本提供的下载按钮,就可以下载到正确格式的图片文件。其他的淘宝、天猫电商图片批量下载,youtube、B站封面下载,等等都可以的。点击右键-tampermonkey-图片下载器-打开脚本(或alt+w),按这个顺序使用。(目前只适合chrome+tampermonkey,其他组合多少有问题)

目前为 2021-09-14 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Image Downloader
  3. // @name:zh-CN 图片下载器
  4. // @name:zh-TW 图片下载器
  5. // @namespace http://tampermonkey.net/
  6. // @description You can extract and download pictures in batches on most websites.Right Click-tampermonkey-Image Downloader(or alt+w),use in this order.(currently only suitable for tampermonkey and chrome,others have not tried it)
  7. // @description:zh-CN 可以在绝大多数网站提取并批量下载图片。尤其是类似于千库网、包图网这种,不能右键保存图片或者保存的图片文件格式无法识别,均可以用脚本提取,然后用脚本提供的下载按钮,就可以下载到正确格式的图片文件。其他的淘宝、天猫电商图片批量下载,youtube、B站封面下载,等等都可以的。点击右键-tampermonkey-图片下载器-打开脚本(或alt+w),按这个顺序使用。(目前只适合chrome+tampermonkey,其他组合多少有问题)
  8. // @description:zh-TW 可以在绝大多数网站提取并批量下载图片。尤其是类似于千库网、包图网这种,不能右键保存图片或者保存的图片文件格式无法识别,均可以用脚本提取,然后用脚本提供的下载按钮,就可以下载到正确格式的图片文件。其他的淘宝、天猫电商图片批量下载,youtube、B站封面下载,等等都可以的。点击右键-tampermonkey-图片下载器-打开脚本(或alt+w),按这个顺序使用。(目前只适合chrome+tampermonkey,其他组合多少有问题)
  9. // @version 1.58
  10. // @author 桃源隐叟
  11. // @include *
  12. // @grant GM_openInTab
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @grant GM_download
  17. // @require https://cdn.jsdelivr.net/npm/hotkeys-js@3.7.2/dist/hotkeys.min.js
  18. // @run-at document-end
  19. // @match *
  20. // @match https://www.bilibili.com/
  21. // @match https://588ku.com/
  22. // @homepageURL https://github.com/taoyuancun123/modifyText/blob/master/modifyText.js
  23. // @supportURL https://greasyfork.org/zh-CN/scripts/419894/feedback
  24. // ==/UserScript==
  25.  
  26. (function () {
  27. 'use strict';
  28.  
  29. var lang = navigator.appName == "Netscape" ? navigator.language : navigator.userLanguage;
  30. var langSet;
  31. var localization = {
  32. zh: {
  33. selectAll: "全选",
  34. downloadBtn: "下载",
  35. downloadMenuText:"打开脚本(Alt+w)"
  36. },
  37. en:{
  38. selectAll: "selectAll",
  39. downloadBtn: "download",
  40. downloadMenuText:"Open(Alt+w)"
  41. }
  42. }
  43.  
  44. if(lang.toLowerCase().includes("zh-")){
  45. langSet=localization.zh;
  46. }else{
  47. langSet=localization.en;
  48. }
  49.  
  50. GM_registerMenuCommand(langSet.downloadMenuText, wrapper);
  51. hotkeys('alt+w', wrapper);
  52.  
  53. function wrapper() {
  54.  
  55. try {
  56. document.querySelector(".tyc-image-container").remove();
  57. } catch {
  58.  
  59. }
  60. var imgUrls = [];
  61. var bodyStr = document.body.innerHTML;
  62. var imgSelected = [];
  63. var imgWaitDownload = [];
  64. var widthFilter = { min: 0, max: 3000 };
  65. var heightFilter = { min: 0, max: 3000 };
  66. var tempImgUrls = [];
  67.  
  68. try {
  69. let imgEles = document.getElementsByTagName("img")
  70.  
  71. for (let i = 0; i < imgEles.length; i++) {
  72. ////console.log(imgEles[i].src);
  73. if (!imgUrls.includes(imgEles[i].src)) {
  74. imgUrls.push(imgEles[i].src);
  75. }
  76. }
  77. } catch {
  78. //alert("error");
  79. }
  80.  
  81. try {
  82. let imgRegs = bodyStr.match(/(?<=background-image:\s*url\()(\S+)(?=\))/g);
  83.  
  84. for (let i = 0; i < imgRegs.length; i++) {
  85. ////console.log(imgRegs[i]);
  86. if (!imgUrls.includes(imgRegs[i].replace(/&quot;/g, ""))) {
  87. imgUrls.push(imgRegs[i].replace(/&quot;/g, ""));
  88. }
  89. }
  90. } catch {
  91. //alert("error");
  92. }
  93.  
  94.  
  95. let imgContainer = `
  96. <div class="tyc-image-container" style="position:fixed;
  97. top:0px;right:0px;width:50vw;z-index:2147483645;
  98. background-color: #dedede;
  99. border: 1px solid #aaa;
  100. overflow:scroll;height:100%;
  101. ">
  102. <div class="control-section" style="width:46vw;z-index:2147483646;
  103. position:fixed;right:30px;
  104. top:0px;display:block;
  105. height:80px;line-height:40px;
  106. background:#eee;border:1px solid #aaa;border-radius:2px;">
  107.  
  108. <input class="select-all" type="checkbox" name="select-all" value="select-all">${langSet.selectAll}
  109. <button class="btn-download" style="border:1px solid #aaa;border-radius:5px;
  110. height:32px;line-height:32px;
  111. margin:0px;padding:0 5px;
  112. ">${langSet.downloadBtn}</button>
  113. <span style="margin-left:10px;" class="num-tip">已选(0/${imgUrls.length})张图片</span>
  114. <button class="btn-close" style="font-size:20px;position:absolute;
  115. right:30px;top:4px;
  116. height:32px;line-height:32px;
  117. margin:0px;
  118. border-radius:10px;border:1px solid #aaa;
  119. width:30px;">X</button>
  120.  
  121. <p style="line-height:12px;">
  122. <div style="float:left;">
  123. <input type="checkbox" class="width-check img-check" name="width-check" value="width-check">Width:
  124. <input type="text" class="width-value-min" size="1" style="height:15px;width:50px;"
  125. min="0" max="9999" value="0">-
  126. <input type="text" class="width-value-max" size="1" style="height:15px;width:50px;"
  127. min="0" max="9999" value="3000">
  128. </div>
  129.  
  130. <div style="float:left;margin-left:30px;">
  131. <input type="checkbox" class="height-check img-check" name="height-check" value="height-check">Height:
  132. <input type="text" class="height-value-min" size="1" style="height:15px;width:50px;"
  133. min="0" max="9999" value="0">-
  134. <input type="text" class="height-value-max" size="1" style="height:15px;width:50px;"
  135. min="0" max="9999" value="3000">
  136. </div>
  137. </p>
  138.  
  139. </div>
  140. <div class="tyc-image-wrapper" style="margin-top:82px;display:flex;justify-content:center;
  141. align-items:center;flex-wrap:wrap;">
  142. </div>
  143. </div>
  144. `
  145.  
  146. let showBigImage = `
  147. <div class="show-big-image" style="position:fixed;left:30%;top:30%;z-index:2147483647;">
  148. </div>
  149. `
  150.  
  151. document.body.insertAdjacentHTML("afterbegin", imgContainer);
  152.  
  153.  
  154.  
  155. if (window.location.href.includes("hathitrust.org")) {
  156. //imgUrls=[];
  157.  
  158. let imgs = document.querySelectorAll(".image img");
  159. if (imgs.length > 0) {
  160. let canvas = document.createElement("canvas");
  161. imgUrls = [];
  162. for (let pi = 0; pi < imgs.length; pi++) {
  163. canvas.width = imgs[pi].width;
  164. canvas.height = imgs[pi].height;
  165.  
  166. canvas.getContext("2d").drawImage(imgs[pi], 0, 0);
  167.  
  168. imgUrls.push(canvas.toDataURL("image/png"));
  169. }
  170.  
  171. document.querySelector(".select-all").style = "position:relative;width:15px;height:15px;"
  172. } else {
  173.  
  174. }
  175. }
  176.  
  177.  
  178.  
  179. document.body.onclick = (e) => {
  180. //console.log(e);
  181. if ((e.target.nodeName == "IMG" && e.target.className === "tyc-image-preview")) {
  182. let imgContainer = e.path.find(
  183.  
  184. (ele) => {
  185. try {
  186. //console.log(ele);
  187. return ele.className.includes("tyc-img-item-container");
  188. }
  189. catch {
  190.  
  191. }
  192.  
  193. }
  194. )
  195. let path = imgContainer.getElementsByTagName("img")[0].src;
  196.  
  197. try {
  198. let container = document.querySelector(".show-big-image");
  199. if (container.getElementsByTagName("img")[0].src === path) {
  200. container.remove();
  201. return;
  202. } else {
  203. container.remove();
  204. }
  205. }
  206. catch {
  207.  
  208. }
  209. document.body.insertAdjacentHTML("beforeend", showBigImage);
  210.  
  211. let showItem = `<img src="${path}"/>`
  212.  
  213. document.querySelector(".show-big-image").insertAdjacentHTML("beforeend", showItem);
  214.  
  215. let tempImg = document.querySelector(".show-big-image img");
  216.  
  217. let dWidth = (window.innerWidth - tempImg.width) / 2;
  218. let dHeight = (window.innerHeight - tempImg.height) / 2;
  219.  
  220. document.querySelector(".show-big-image").style.left = dWidth + "px";
  221. document.querySelector(".show-big-image").style.top = dHeight + "px";
  222. } else if (e.target.parentElement.className === "show-big-image") {
  223. try {
  224. document.querySelector(".show-big-image").remove();
  225. }
  226. catch
  227. {
  228.  
  229. }
  230.  
  231. } else if (e.target.classList[1] == "bi-download" || e.path.find(isDownload) != undefined) {
  232. let imgContainer = e.path.find(
  233.  
  234. (ele) => {
  235. try {
  236. //console.log(ele);
  237. return ele.className.includes("tyc-img-item-container");
  238. }
  239. catch {
  240.  
  241. }
  242.  
  243. }
  244. )
  245. let path = imgContainer.getElementsByTagName("img")[0].src;
  246. var filename;
  247. if (path.indexOf("/") > 0)//如果包含有"/"号 从最后一个"/"号+1的位置开始截取字符串
  248. {
  249. filename = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
  250. }
  251. else {
  252. filename = path;
  253. }
  254. //console.log("download start" + path + " " + filename);
  255. GM_download(path, "pic");
  256. } else if (e.target.classList[1] == "bi-check" || e.path.find(isSelect) != undefined) {
  257. let checkSvg = e.path.find((ele) => ele.classList[1] === "bi-check");
  258. let currentImg = parseInt(checkSvg.dataset.value);
  259.  
  260. let container = e.path.find((ele) => ele.className === `tyc-img-item-container-${currentImg}`);
  261.  
  262.  
  263.  
  264. if (imgSelected.includes(currentImg)) {
  265. imgSelected.splice(imgSelected.indexOf(currentImg), 1);
  266. checkSvg.style.color = "black";
  267. container.style.border = "1px solid #99d";
  268. } else {
  269. imgSelected.push(currentImg);
  270. checkSvg.style.color = "white";
  271. container.style.border = "1px solid white";
  272. }
  273.  
  274. document.querySelector(".num-tip").innerText = `已选(${imgSelected.length}/${imgUrls.length})张图片`;
  275. transIndexToLink(tempImgUrls);
  276. }
  277. }
  278.  
  279.  
  280. document.querySelector(".btn-close").onclick = (e) => {
  281. document.querySelector(".tyc-image-container").remove();
  282. }
  283.  
  284. document.querySelector(".btn-download").onclick = (e) => {
  285. /* if (document.querySelector(".select-all").checked) {
  286. imgUrls.forEach((img, index) => {
  287. GM_download(img, `pic-${index}`);
  288. });
  289. } else {
  290. alert("请勾选全选,下载全部,或者手动在图片上右键另存为指定图片");
  291. } */
  292. if (imgWaitDownload.length >= 1) {
  293. imgWaitDownload.forEach(async (img, index) => {
  294. console.log(index);
  295. console.log(img);
  296. await GM_download(img, `pic-${index}`);
  297. });
  298. } else {
  299. alert("请至少选中一张图片。");
  300. }
  301. }
  302.  
  303. document.body.onchange = (e) => {
  304. if (e.target.className.includes("width-check")) {
  305. GM_setValue('width-check', e.target.checked);
  306. }
  307. if (e.target.className.includes("height-check")) {
  308. GM_setValue('height-check', e.target.checked);
  309. }
  310. if (e.target.nodeName === "INPUT" && e.target.type === "text" && e.target.className.includes("value")) {
  311. GM_setValue(e.target.className, e.target.value);
  312. }
  313.  
  314. (e.target.className.includes("width-check") || e.target.className.includes("height-check") ||
  315. (e.target.nodeName === "INPUT" && e.target.type === "text" && e.target.className.includes("value")))
  316. && (clean(), init());
  317.  
  318.  
  319. }
  320.  
  321. document.querySelector(".select-all").onchange = (e) => {
  322. if (document.querySelector(".select-all").checked) {
  323. imgWaitDownload = tempImgUrls;
  324. } else {
  325. transIndexToLink(tempImgUrls);
  326. }
  327.  
  328. document.querySelector(".num-tip").innerText = `已选(${imgWaitDownload.length}/${imgUrls.length})张图片`;
  329. }
  330.  
  331. init();
  332. function init() {
  333. tempImgUrls = imgUrls;
  334. getSavedValue();
  335. if (document.querySelector(".width-check").checked) {
  336. tempImgUrls = tempImgUrls.filter(filterByWidth);
  337. }
  338.  
  339. if (document.querySelector(".height-check").checked) {
  340. tempImgUrls = tempImgUrls.filter(filterByHeight);
  341. }
  342.  
  343. showImage(tempImgUrls);
  344. }
  345.  
  346. function clean() {
  347. imgWaitDownload = [];
  348. imgSelected = [];
  349. document.querySelector(".num-tip").innerText = `已选(${imgSelected.length}/${imgUrls.length})张图片`;
  350. document.querySelector(".tyc-image-wrapper").innerHTML = "";
  351. }
  352.  
  353. function isDownload(ele) {
  354. return ele.className == "download-direct";
  355. }
  356.  
  357. function isSelect(ele) {
  358. return ele.className == "select-image";
  359. }
  360.  
  361. function transIndexToLink(tempImgUrls) {
  362. imgWaitDownload = [];
  363. imgSelected.forEach((imgIndex, index) => {
  364. imgWaitDownload.push(tempImgUrls[imgIndex]);
  365. });
  366. }
  367.  
  368. function showImage(filtedImgUrls) {
  369. filtedImgUrls.forEach((img, index) => {
  370. if (window.location.href.includes("huaban.com")) {
  371. if (img.includes("/webp")) {
  372. img = img.replace(/\/webp/g, "/png");
  373. }
  374.  
  375. }
  376. let insertImg = `<div class="tyc-img-item-container-${index}" style="text-align:center;font-size:0px;
  377. margin:5px;border:1px solid #99d;border-radius:3px;
  378. ">
  379. <img class="tyc-image-preview" src="${img}"/ style="width:auto;height:200px;"></div>`
  380. document.querySelector(".tyc-image-wrapper").insertAdjacentHTML("beforeend", insertImg);
  381. let naturalW = document.querySelector(`.tyc-img-item-container-${index} .tyc-image-preview`).naturalWidth;
  382. let naturalH = document.querySelector(`.tyc-img-item-container-${index} .tyc-image-preview`).naturalHeight;
  383.  
  384. let imgInfoContainer = `
  385. <div style="font-size:0px;background-color:rgba(100,100,100,0.6);height:30px;position:relative;">
  386.  
  387.  
  388. </div>
  389. `;
  390.  
  391. let thisImgContainer = document.querySelector(`.tyc-img-item-container-${index}`);
  392. let imgContainerWidth = thisImgContainer.getBoundingClientRect().width;
  393. let imgInfo = `
  394. <span style="font-size:16px;position:absolute;left:calc(50% - 80px);top:7px;">${naturalW}X${naturalH}</span>
  395. `;
  396.  
  397.  
  398. /*
  399. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrows-fullscreen" viewBox="0 0 16 16" style="position:absolute;top:5px;right:5px;">
  400. <path fill-rule="evenodd" d="M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 0 0-1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707zm4.344 0a.5.5 0 0 1 .707 0l4.096 4.096V11.5a.5.5 0 1 1 1 0v3.975a.5.5 0 0 1-.5.5H11.5a.5.5 0 0 1 0-1h2.768l-4.096-4.096a.5.5 0 0 1 0-.707zm0-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707zm-4.344 0a.5.5 0 0 1-.707 0L1.025 1.732V4.5a.5.5 0 0 1-1 0V.525a.5.5 0 0 1 .5-.5H4.5a.5.5 0 0 1 0 1H1.732l4.096 4.096a.5.5 0 0 1 0 .707z"/>
  401. </svg>*/
  402.  
  403. let downAndFullBtn = `
  404. <span style="position:absolute;right:calc(50% - 30px);top:2px;border:1px solid #333;
  405. width:26px;height:26px;border-radius:20px;" class="select-image" data-value="${index}">
  406. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16" style="position:absolute;top:-1px;right:-2px;width:30px;height:30px;" data-value="${index}">
  407. <path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
  408. </svg>
  409. </span>
  410. <span style="position:absolute;right:calc(50% - 60px);top:2px;border:1px solid #333;
  411. width:26px;height:26px;border-radius:20px;
  412. " class="download-direct">
  413. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16" style="position:absolute;top:5px;right:5px;">
  414. <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
  415. <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
  416. </svg>
  417. </span>
  418.  
  419. `;
  420.  
  421. let downloadBtn = `
  422. <span style="position:absolute;right:calc(50% - 15px);top:2px;border:1px solid #333;
  423. width:26px;height:26px;border-radius:20px;
  424. " class="download-direct">
  425. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16" style="position:absolute;top:5px;right:5px;">
  426. <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
  427. <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
  428. </svg>
  429. </span>
  430. `
  431.  
  432.  
  433. thisImgContainer.insertAdjacentHTML("beforeend", imgInfoContainer);
  434.  
  435. let thisImgInfoContainer = thisImgContainer.querySelector("div");
  436.  
  437. let rectWidth = parseInt(thisImgContainer.getBoundingClientRect().width);
  438.  
  439. if (rectWidth > 120) {
  440. thisImgInfoContainer.insertAdjacentHTML("beforeend", imgInfo);
  441. thisImgInfoContainer.insertAdjacentHTML("beforeend", downAndFullBtn);
  442. } else if (rectWidth <= 120 && rectWidth >= 50) {
  443. thisImgInfoContainer.insertAdjacentHTML("beforeend", downAndFullBtn);
  444. thisImgInfoContainer.getElementsByClassName("select-image")[0].style.right = "50%";
  445. thisImgInfoContainer.getElementsByClassName("download-direct")[0].style.right = "calc(50% - 30px)";
  446.  
  447.  
  448. } else {
  449. thisImgInfoContainer.insertAdjacentHTML("beforeend", downloadBtn);
  450. }
  451.  
  452.  
  453. ////console.log(img);
  454. });
  455. }
  456.  
  457. function filterByWidth(src) {
  458. let tempImg = new Image();
  459. tempImg.src = src;
  460. if (tempImg.width >= parseInt(document.querySelector(".width-value-min").value)
  461. && tempImg.width <= parseInt(document.querySelector(".width-value-max").value)) {
  462. return src;
  463. }
  464. }
  465.  
  466. function filterByHeight(src) {
  467. let tempImg = new Image();
  468. tempImg.src = src;
  469. if (tempImg.height >= parseInt(document.querySelector(".height-value-min").value)
  470. && tempImg.height <= parseInt(document.querySelector(".height-value-max").value)) {
  471. return src;
  472. }
  473. }
  474.  
  475. function getSavedValue() {
  476. GM_getValue("width-check") && (document.querySelector(".width-check").checked = GM_getValue("width-check"));
  477. GM_getValue("height-check") && (document.querySelector(".height-check").checked = GM_getValue("height-check"));
  478. GM_getValue("width-value-min") && (document.querySelector(".width-value-min").value = GM_getValue("width-value-min"));
  479. GM_getValue("width-value-max") && (document.querySelector(".width-value-max").value = GM_getValue("width-value-max"));
  480. GM_getValue("height-value-min") && (document.querySelector(".height-value-min").value = GM_getValue("height-value-min"));
  481. GM_getValue("height-value-max") && (document.querySelector(".height-value-max").value = GM_getValue("height-value-max"));
  482. }
  483. }
  484.  
  485. })();