DuckDuckGo URL Collector

Collects all URLs from DuckDuckGo search results with logging and duplicate prevention

当前为 2025-01-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name DuckDuckGo URL Collector
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Collects all URLs from DuckDuckGo search results with logging and duplicate prevention
  6. // @author Ghosty-Tongue
  7. // @match *://duckduckgo.com/*
  8. // @grant GM_notification
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. const collectedUrls = new Set();
  16. let isProcessing = false;
  17. let startTime, timerInterval;
  18.  
  19. const style = document.createElement('style');
  20. style.textContent = `
  21. @keyframes rgbFlow {
  22. 0% { background-position: 0% 50%; }
  23. 100% { background-position: 100% 50%; }
  24. }
  25. @keyframes pulse {
  26. 0% { transform: scale(1); }
  27. 50% { transform: scale(1.1); }
  28. 100% { transform: scale(1); }
  29. }
  30. `;
  31. document.head.appendChild(style);
  32.  
  33. const timerDisplay = document.createElement('div');
  34. Object.assign(timerDisplay.style, {
  35. position: 'fixed',
  36. top: '50px',
  37. right: '10px',
  38. zIndex: '10000',
  39. color: 'white',
  40. backgroundColor: 'rgba(0,0,0,0.7)',
  41. padding: '5px 10px',
  42. borderRadius: '5px',
  43. fontFamily: 'Arial, sans-serif',
  44. fontSize: '14px'
  45. });
  46. document.body.appendChild(timerDisplay);
  47.  
  48. function startTimer() {
  49. if (timerInterval) clearInterval(timerInterval);
  50. startTime = Date.now();
  51. timerInterval = setInterval(updateTimer, 1000);
  52. timerDisplay.textContent = '0s';
  53. }
  54.  
  55. function updateTimer() {
  56. const elapsed = Math.floor((Date.now() - startTime) / 1000);
  57. timerDisplay.textContent = `${elapsed}s`;
  58. }
  59.  
  60. function stopTimer() {
  61. clearInterval(timerInterval);
  62. const elapsed = Math.floor((Date.now() - startTime) / 1000);
  63. timerDisplay.textContent = `${elapsed}s (stopped)`;
  64. }
  65.  
  66. function extractUrls() {
  67. console.log('Extracting URLs from current page...');
  68. const results = document.querySelectorAll('article[data-testid="result"]');
  69. let newUrlsCount = 0;
  70.  
  71. results.forEach(result => {
  72. const link = result.querySelector('a[data-testid="result-extras-url-link"]');
  73. if (link) {
  74. const url = link.href;
  75. if (!collectedUrls.has(url)) {
  76. collectedUrls.add(url);
  77. newUrlsCount++;
  78. }
  79. }
  80. });
  81.  
  82. console.log(`Added ${newUrlsCount} new URLs from this batch. Total: ${collectedUrls.size}`);
  83. return newUrlsCount;
  84. }
  85.  
  86. async function clickMoreResults() {
  87. isProcessing = true;
  88. btn.classList.add('processing');
  89. console.log('Starting URL collection process...');
  90.  
  91. let iteration = 1;
  92. let moreResultsButton;
  93.  
  94. do {
  95. console.log(`Looking for "More Results" button (Attempt ${iteration})...`);
  96. moreResultsButton = document.getElementById('more-results');
  97.  
  98. if (moreResultsButton) {
  99. console.log('Clicking "More Results" button...');
  100. moreResultsButton.click();
  101. await new Promise(resolve => setTimeout(resolve, 2000));
  102. extractUrls();
  103. iteration++;
  104. }
  105. } while (moreResultsButton);
  106.  
  107. console.log(`Finished collecting URLs. Total unique URLs: ${collectedUrls.size}`);
  108. isProcessing = false;
  109. btn.classList.remove('processing');
  110.  
  111. GM_notification({
  112. title: 'Collection Complete',
  113. text: `Saved ${collectedUrls.size} URLs`,
  114. timeout: 5000
  115. });
  116.  
  117. saveUrls();
  118. }
  119.  
  120. function saveUrls() {
  121. console.log('Preparing to save URLs to file...');
  122. const blob = new Blob([Array.from(collectedUrls).join('\n')], {type: 'text/plain'});
  123. const url = URL.createObjectURL(blob);
  124. const a = document.createElement('a');
  125. a.href = url;
  126. a.download = 'urls.txt';
  127. document.body.appendChild(a);
  128. a.click();
  129. document.body.removeChild(a);
  130. URL.revokeObjectURL(url);
  131. console.log(`File saved with ${collectedUrls.size} URLs`);
  132. stopTimer();
  133. }
  134.  
  135. const btn = document.createElement('button');
  136. btn.textContent = '🦆';
  137. Object.assign(btn.style, {
  138. position: 'fixed',
  139. top: '10px',
  140. right: '10px',
  141. zIndex: '10000',
  142. padding: '12px 24px',
  143. background: 'linear-gradient(90deg, #ff0000, #00ff00, #0000ff, #ff0000)',
  144. backgroundSize: '300% 100%',
  145. animation: 'rgbFlow 5s linear infinite',
  146. color: 'white',
  147. border: 'none',
  148. borderRadius: '25px',
  149. cursor: 'pointer',
  150. fontFamily: 'Arial, sans-serif',
  151. fontWeight: 'bold',
  152. boxShadow: '0 4px 15px rgba(0,0,0,0.2)',
  153. transition: 'transform 0.2s, box-shadow 0.2s'
  154. });
  155.  
  156. btn.addEventListener('mouseover', () => {
  157. btn.style.transform = 'scale(1.05)';
  158. btn.style.boxShadow = '0 6px 20px rgba(0,0,0,0.25)';
  159. });
  160.  
  161. btn.addEventListener('mouseout', () => {
  162. btn.style.transform = 'scale(1)';
  163. btn.style.boxShadow = '0 4px 15px rgba(0,0,0,0.2)';
  164. });
  165.  
  166. btn.addEventListener('click', () => {
  167. if (!isProcessing) {
  168. console.log('----- New Collection Started -----');
  169. collectedUrls.clear();
  170. startTimer();
  171. clickMoreResults();
  172. }
  173. });
  174.  
  175. document.body.appendChild(btn);
  176. console.log('URL Collector button initialized');
  177. })();