IEEE Paper Downloader

IEEE Paper Downloader is a Greasemonkey script that enables batch downloading of papers. It bypasses the IEEE Xplore's limit on downloading up to 10 papers at a time, allowing you to download more than 10 papers in one go.

  1. // ==UserScript==
  2. // @name IEEE Paper Downloader
  3. // @namespace https://ieeexplore.ieee.org/
  4. // @version 1.2
  5. // @description IEEE Paper Downloader is a Greasemonkey script that enables batch downloading of papers. It bypasses the IEEE Xplore's limit on downloading up to 10 papers at a time, allowing you to download more than 10 papers in one go.
  6. // @author OccDeser
  7. // @match https://ieeexplore.ieee.org/*/proceeding*
  8. // @grant GM_xmlhttpRequest
  9. // @grant GM_download
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. const DOWNLOAD_INTERVAL = 1000; // Download interval in milliseconds
  14.  
  15. const CSS = `
  16. .paper-downloader {
  17. margin-left: 15px;
  18. margin-right: 15px;
  19. padding-top: 15px;
  20. padding-bottom: 15px;
  21. background-color: #f5f5f5;
  22. }
  23.  
  24. .paper-downloader span {
  25. padding: 5px;
  26. }
  27.  
  28. .ipd-modal-background {
  29. position: fixed;
  30. top: 0;
  31. left: 0;
  32. width: 100%;
  33. height: 100%;
  34. background: rgba(0, 0, 0, 0.5); /* Gray background with 50% transparency */
  35. display: flex;
  36. justify-content: center; /* Horizontal centering */
  37. align-items: center; /* Vertical centering */
  38. z-index: 999; /* Place modal on top */
  39. flex-direction: column;
  40. }
  41.  
  42. .ipd-modal-body {
  43. width: 75%;
  44. padding: 20px;
  45. background: #fff;
  46. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  47. }
  48.  
  49. .ipd-modal-content {
  50. height: 450px;
  51. overflow-y: auto;
  52. }
  53. `
  54.  
  55. const HTML = `
  56. <div class="paper-downloader">
  57. <span>
  58. <xpl-conference-toc-dashboard>
  59. <strong class="text-base-md-lh">
  60. IEEE Paper Downloader
  61. </strong>
  62. </xpl-conference-toc-dashboard>
  63. </span>
  64. <span>
  65. <xpl-conference-toc-dashboard>
  66. <strong class="text-base-md-lh" id="ipd-strong-countdown">
  67. </strong>
  68. </xpl-conference-toc-dashboard>
  69. </span>
  70. <span>
  71. <xpl-export-search-results>
  72. <button class="xpl-toggle-btn" id="ipd-button-showall">
  73. <a class="text-white">
  74. Show All
  75. </a>
  76. </button>
  77. </xpl-export-search-results>
  78. </span>
  79. <span>
  80. <xpl-export-search-results>
  81. <button class="xpl-toggle-btn" id="ipd-button-select">
  82. <a class="text-white">
  83. Select
  84. </a>
  85. </button>
  86. </xpl-export-search-results>
  87. </span>
  88. <span>
  89. <xpl-export-search-results>
  90. <button class="xpl-toggle-btn" id="ipd-button-download">
  91. <a class="text-white">
  92. Download
  93. </a>
  94. </button>
  95. </xpl-export-search-results>
  96. </span>
  97. </div>
  98. `
  99.  
  100. var allPapers = {};
  101. var selectedPapers = {};
  102.  
  103. const createModal = (content, onClose) => {
  104. const modalHtml = `
  105. <div class="ipd-modal-background">
  106. <div class="ipd-modal-body">
  107. <div class="ipd-modal-content">
  108. ${content}
  109. </div>
  110. <xpl-export-search-results>
  111. <button class="xpl-toggle-btn" id="ipd-modal-close">
  112. <a class="text-white">Close</a>
  113. </button>
  114. </xpl-export-search-results>
  115. </div>
  116. </div>
  117. `
  118.  
  119. let modalElement = document.createElement('div');
  120. modalElement.innerHTML = modalHtml;
  121. document.body.appendChild(modalElement);
  122.  
  123. // Bind listener
  124. let modalCloseButton = document.getElementById('ipd-modal-close');
  125. modalCloseButton.addEventListener('click', () => {
  126. modalElement.remove();
  127. onClose();
  128. });
  129. }
  130.  
  131. const downloadPapers = () => {
  132. const downloadLinks = [];
  133. for (let id in selectedPapers) {
  134. if (selectedPapers[id]) {
  135. let url = new URL("https://ieeexplore.ieee.org" + allPapers[id].href);
  136. const arnumber = url.searchParams.get('arnumber');
  137. downloadLinks.push(`https://ieeexplore.ieee.org/stampPDF/getPDF.jsp?tp=&arnumber=${arnumber}&isBulkDownload=true`);
  138. }
  139. }
  140. console.log(downloadLinks);
  141.  
  142. // Show download progress
  143. createModal(
  144. '<div id="ipd-modal-progress"></div>',
  145. () => {
  146. console.log('Modal closed');
  147. }
  148. );
  149.  
  150. let progress = document.getElementById('ipd-modal-progress');
  151. let progressCounter = 0;
  152.  
  153. const progressInfo = (message) => {
  154. let messageBox = document.createElement('div');
  155. messageBox.innerHTML = message;
  156. messageBox.style = "margin-bottom: 5px;";
  157. progress.insertBefore(messageBox, progress.firstChild);
  158. }
  159.  
  160. const progressTop = (message) => {
  161. let topMessageBox = document.getElementById('ipd-modal-progress-top');
  162. if (topMessageBox) {
  163. topMessageBox.remove();
  164. }
  165.  
  166. let messageBox = document.createElement('div');
  167. messageBox.id = 'ipd-modal-progress-top';
  168. messageBox.innerHTML = message;
  169. messageBox.style = "margin-bottom: 5px; font-weight: bold;";
  170. progress.insertBefore(messageBox, progress.firstChild);
  171. }
  172. progressInfo(`Downloading ${downloadLinks.length} papers...`);
  173.  
  174. // Store downloaded content
  175. const downloadedContent = [];
  176.  
  177. // Use GM_xmlhttpRequest to fetch link content
  178. const fetchContent = (url) => {
  179. let filename = new URL(url).searchParams.get('arnumber') + '.pdf';
  180.  
  181. return new Promise((resolve, reject) => {
  182. GM_xmlhttpRequest({
  183. method: 'GET',
  184. url: url,
  185. responseType: 'arraybuffer',
  186. onload: response => resolve(response.response),
  187. onerror: error => reject(error),
  188. onprogress: progressEvent => {
  189. let size = progressEvent.loaded / 1024 / 1024;
  190. progressTop(`${filename} Downloading: ${size.toFixed(2)}MB`);
  191. }
  192. });
  193. });
  194. }
  195.  
  196. // Iterate through download links and fetch content
  197. const fetchAndDownload = async () => {
  198. for (const link of downloadLinks) {
  199. try {
  200. let filename = new URL(link).searchParams.get('arnumber') + '.pdf';
  201. const content = await fetchContent(link);
  202. downloadedContent.push({ filename: filename, content });
  203. progressCounter++;
  204. progressInfo(`[${progressCounter}/${downloadLinks.length}] Fetched content from: ${link}`);
  205. progressTop(`${filename} Downloaded, continuing in ${DOWNLOAD_INTERVAL / 1000} seconds...`);
  206. await new Promise(resolve => setTimeout(resolve, DOWNLOAD_INTERVAL));
  207. } catch (error) {
  208. console.error(`Error fetching content from link: ${error}`);
  209. }
  210. }
  211.  
  212. // Compress all content into a single zip file and download
  213. const zip = new JSZip();
  214. for (const item of downloadedContent) {
  215. zip.file(item.filename, item.content);
  216. }
  217.  
  218. zip.generateAsync({ type: 'blob' })
  219. .then(blob => {
  220. GM_download({
  221. url: URL.createObjectURL(blob),
  222. name: 'papers.zip'
  223. });
  224. });
  225. }
  226.  
  227. fetchAndDownload();
  228. }
  229.  
  230. const selectPapers = () => {
  231. const xpathExpression = '//div[@class="subid-container"]//xpl-access-type-icon/span[not(contains(@class, "locked"))]/../../../..';
  232. let paperResults = document.evaluate(xpathExpression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  233. if (paperResults.snapshotLength > 0) {
  234. let paperElement = paperResults.snapshotItem(0);
  235. let paperHref = paperElement.getElementsByTagName('xpl-view-pdf')[0].getElementsByTagName('a')[0].getAttribute('href');
  236. let paperTitle = paperElement.getElementsByTagName('h2')[0].textContent;
  237. let id = 0 + '-' + paperTitle.slice(0, 16) + '-' + paperHref.slice(-8);
  238. let first_id = Object.keys(allPapers).filter((key) => key.startsWith('0'))[0];
  239.  
  240. // Check if the page has been updated
  241. if (Object.keys(allPapers).length !== paperResults.snapshotLength || id !== first_id) {
  242. allPapers = {};
  243. selectedPapers = {};
  244. for (let i = 0; i < paperResults.snapshotLength; i++) {
  245. let paperElement = paperResults.snapshotItem(i);
  246. let paperHref = paperElement.getElementsByTagName('xpl-view-pdf')[0].getElementsByTagName('a')[0].getAttribute('href');
  247. let paperTitle = paperElement.getElementsByTagName('h2')[0].textContent;
  248. let id = i + '-' + paperTitle.slice(0, 16) + '-' + paperHref.slice(-8);
  249. allPapers[id] = {
  250. 'title': paperTitle,
  251. 'href': paperHref,
  252. 'element': paperElement
  253. };
  254. selectedPapers[id] = true;
  255. }
  256. }
  257. }
  258.  
  259. // Create modal
  260. createModal(
  261. '<form id="ipd-select-dynamic-form"></form>',
  262. () => {
  263. let counter = 0;
  264. for (let id in selectedPapers) {
  265. if (selectedPapers[id]) {
  266. counter++;
  267. }
  268. }
  269. console.log(`Selected ${counter} papers`);
  270. }
  271. )
  272.  
  273. // Create select form
  274. let dynamicForm = document.getElementById('ipd-select-dynamic-form');
  275. let items = []
  276. for (let id in allPapers) {
  277. items.push({ id, title: allPapers[id].title });
  278. }
  279. items.forEach(function (item, _) {
  280. var checkbox = document.createElement('input');
  281. checkbox.type = 'checkbox';
  282. checkbox.name = item.title;
  283. checkbox.value = item.id;
  284. checkbox.checked = selectedPapers[item.id];
  285. checkbox.addEventListener('change', function () {
  286. selectedPapers[checkbox.value] = checkbox.checked;
  287. });
  288.  
  289. var label = document.createElement('label');
  290. label.appendChild(checkbox);
  291.  
  292. var titleBox = document.createElement('span');
  293. titleBox.appendChild(document.createTextNode(item.title));
  294. titleBox.style = "margin-left: 5px;";
  295. label.appendChild(titleBox);
  296.  
  297. var itemDiv = document.createElement('div');
  298. itemDiv.style = "margin-bottom: 5px;";
  299. itemDiv.appendChild(label);
  300.  
  301. dynamicForm.appendChild(itemDiv);
  302. });
  303. }
  304.  
  305. const showAll = () => {
  306. console.log("showAll");
  307.  
  308. // Get the current rowsPerPage
  309. const url = new URL(window.location.href);
  310. const current_rows = eval(url.searchParams.get('rowsPerPage'))
  311.  
  312. // Get the total number of rows
  313. const xpathExpression = "//span[contains(., 'Showing')]/span[2]/text()";
  314. const result = document.evaluate(xpathExpression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  315. if (result.snapshotLength == 0) {
  316. console.log("Cannot find total rows");
  317. return;
  318. }
  319.  
  320. const total_rows = eval(result.snapshotItem(0).textContent);
  321.  
  322. if (current_rows >= total_rows) {
  323. console.log("Already showing all");
  324. return;
  325. }
  326.  
  327. url.searchParams.set('rowsPerPage', total_rows.toString());
  328. const modifiedURL = url.toString();
  329. window.location.href = modifiedURL;
  330. }
  331.  
  332. const bindListener = () => {
  333. let buttonSelect = document.getElementById('ipd-button-select');
  334. let buttonDownload = document.getElementById('ipd-button-download');
  335. let buttonShowAll = document.getElementById('ipd-button-showall');
  336.  
  337. buttonSelect.addEventListener('click', selectPapers);
  338. buttonDownload.addEventListener('click', downloadPapers);
  339. buttonShowAll.addEventListener('click', showAll);
  340. }
  341.  
  342. (function () {
  343. 'use strict';
  344.  
  345. let mainElement = document.getElementById('xplMainContentLandmark');
  346.  
  347. if (mainElement) {
  348. // Insert CSS codes
  349. let style = document.createElement('style');
  350. style.appendChild(document.createTextNode(CSS));
  351. document.head.appendChild(style);
  352.  
  353. // Insert HTML codes
  354. let optionHeader = document.createElement('div');
  355. optionHeader.innerHTML = HTML;
  356. let firstChild = mainElement.firstChild;
  357. mainElement.insertBefore(optionHeader, firstChild);
  358.  
  359. // Add event listener
  360. bindListener();
  361.  
  362. // Set the countdown
  363. let timerCounter = 0;
  364. let countDownTimer = setInterval(() => {
  365. let countDownElement = document.getElementById('ipd-strong-countdown');
  366. timerCounter = timerCounter + 100;
  367. countDownElement.innerHTML = "Loading... " + timerCounter / 1000 + "s";
  368. }, 100);
  369.  
  370. // Wait for loading
  371. const loadPapers = () => {
  372. const xpathExpression = '//div[@class="subid-container"]//xpl-access-type-icon/span[not(contains(@class, "locked"))]/../../../..';
  373. let paperResults = document.evaluate(xpathExpression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  374. if (paperResults.snapshotLength > 0) {
  375. clearInterval(countDownTimer);
  376. let countDownElement = document.getElementById('ipd-strong-countdown');
  377. countDownElement.innerHTML = "Loading complete!";
  378. } else {
  379. setTimeout(loadPapers, 100);
  380. return;
  381. }
  382. }
  383. setTimeout(loadPapers, 100);
  384. }
  385. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址