图片下载器

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

当前为 2021-03-28 提交的版本,查看 最新版本

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