Extract product information with affiliate links and export to CSV
Size
13.7 KB
Version
1.1.1
Created
Nov 8, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name DHgate Affiliate Product Extractor
3// @description Extract product information with affiliate links and export to CSV
4// @version 1.1.1
5// @match https://*.aff.dhgate.com/*
6// @icon https://js.dhresource.com/dhgate/affiliate-publisher/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('DHgate Affiliate Product Extractor loaded');
12
13 // Debounce function to prevent excessive calls
14 function debounce(func, wait) {
15 let timeout;
16 return function executedFunction(...args) {
17 const later = () => {
18 clearTimeout(timeout);
19 func(...args);
20 };
21 clearTimeout(timeout);
22 timeout = setTimeout(later, wait);
23 };
24 }
25
26 // Function to extract product data from the page
27 function extractProductData() {
28 console.log('Starting product extraction...');
29 const products = [];
30
31 // Find all product items on the page
32 const productElements = document.querySelectorAll('.product-item[data-v-f70a41f0]');
33 console.log(`Found ${productElements.length} products`);
34
35 productElements.forEach((product, index) => {
36 try {
37 // Extract product link
38 const linkElement = product.querySelector('a[data-v-f70a41f0][href]');
39 const productUrl = linkElement ? linkElement.getAttribute('href') : '';
40
41 // Extract product title
42 const titleElement = product.querySelector('p.c-333.lh-20.ellipsis-two');
43 const title = titleElement ? titleElement.textContent.trim() : '';
44
45 // Extract current price
46 const priceElement = product.querySelector('span.fs-14.f-b.main-color');
47 const price = priceElement ? priceElement.textContent.trim() : '';
48
49 // Extract original price
50 const originalPriceElement = product.querySelector('span.c-999.td-lt');
51 const originalPrice = originalPriceElement ? originalPriceElement.textContent.trim() : '';
52
53 // Extract commission
54 const commissionElement = product.querySelector('div.c-000.fs-14');
55 const commission = commissionElement ? commissionElement.textContent.trim() : '';
56
57 // Extract commission rate
58 const rateElement = product.querySelector('div.c-999.mt-6.lh-16');
59 const rate = rateElement ? rateElement.textContent.trim() : '';
60
61 // Extract coupon info if available
62 const couponElement = product.querySelector('.coupon span');
63 const coupon = couponElement ? couponElement.textContent.trim() : 'No coupon';
64
65 // Extract image URL
66 const imageElement = product.querySelector('img[data-v-f70a41f0]');
67 const imageUrl = imageElement ? imageElement.getAttribute('src') || imageElement.getAttribute('data-src') : '';
68
69 if (title && productUrl) {
70 products.push({
71 title,
72 price,
73 originalPrice,
74 commission,
75 rate,
76 coupon,
77 affiliateLink: productUrl,
78 imageUrl
79 });
80 console.log(`Extracted product ${index + 1}: ${title}`);
81 }
82 } catch (error) {
83 console.error(`Error extracting product ${index + 1}:`, error);
84 }
85 });
86
87 console.log(`Successfully extracted ${products.length} products`);
88 return products;
89 }
90
91 // Function to convert data to CSV format
92 function convertToCSV(products) {
93 if (products.length === 0) {
94 return '';
95 }
96
97 // CSV headers
98 const headers = ['Title', 'Price', 'Original Price', 'Commission', 'Rate', 'Coupon', 'Affiliate Link', 'Image URL'];
99
100 // Create CSV rows
101 const rows = products.map(product => {
102 return [
103 `"${product.title.replace(/"/g, '""')}"`,
104 `"${product.price}"`,
105 `"${product.originalPrice}"`,
106 `"${product.commission}"`,
107 `"${product.rate}"`,
108 `"${product.coupon}"`,
109 `"${product.affiliateLink}"`,
110 `"${product.imageUrl}"`
111 ].join(',');
112 });
113
114 return [headers.join(','), ...rows].join('\n');
115 }
116
117 // Function to download CSV file
118 function downloadCSV(csvContent, filename) {
119 const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
120 const link = document.createElement('a');
121 const url = URL.createObjectURL(blob);
122
123 link.setAttribute('href', url);
124 link.setAttribute('download', filename);
125 link.style.visibility = 'hidden';
126
127 document.body.appendChild(link);
128 link.click();
129 document.body.removeChild(link);
130
131 console.log(`CSV file "${filename}" downloaded successfully`);
132 }
133
134 // Function to export to Google Sheets
135 function exportToGoogleSheets(products) {
136 console.log('Exporting to Google Sheets...');
137
138 // Create the data array with headers
139 const headers = ['Title', 'Price', 'Original Price', 'Commission', 'Rate', 'Coupon', 'Affiliate Link', 'Image URL'];
140 const rows = products.map(product => [
141 product.title,
142 product.price,
143 product.originalPrice,
144 product.commission,
145 product.rate,
146 product.coupon,
147 product.affiliateLink,
148 product.imageUrl
149 ]);
150
151 const data = [headers, ...rows];
152
153 // Convert to TSV format (Tab Separated Values) for Google Sheets
154 const tsvContent = data.map(row => row.join('\t')).join('\n');
155
156 // Create a Google Sheets URL with the data
157 const encodedData = encodeURIComponent(tsvContent);
158 const sheetsUrl = 'https://docs.google.com/spreadsheets/create?usp=sheets_web#gid=0';
159
160 // Copy data to clipboard
161 navigator.clipboard.writeText(tsvContent).then(() => {
162 console.log('Data copied to clipboard');
163 // Open Google Sheets in a new tab
164 window.open(sheetsUrl, '_blank');
165 alert('Data copied to clipboard! A new Google Sheet will open. Click on cell A1 and paste (Ctrl+V or Cmd+V) to import the data.');
166 }).catch(err => {
167 console.error('Failed to copy to clipboard:', err);
168 alert('Failed to copy data to clipboard. Please try the CSV export instead.');
169 });
170 }
171
172 // Function to create and show extraction button
173 function createExtractionButton() {
174 // Check if button already exists
175 if (document.getElementById('dhgate-extractor-btn')) {
176 console.log('Extraction button already exists');
177 return;
178 }
179
180 // Find the search card area to place the button
181 const searchCard = document.querySelector('.search-card__top[data-v-57e1c60a]');
182 if (!searchCard) {
183 console.log('Search card not found, will retry...');
184 return;
185 }
186
187 // Create button container
188 const buttonContainer = document.createElement('div');
189 buttonContainer.id = 'dhgate-extractor-btn';
190 buttonContainer.style.cssText = `
191 margin-left: 16px;
192 display: inline-flex;
193 gap: 8px;
194 align-items: center;
195 `;
196
197 // Create the CSV export button
198 const extractButton = document.createElement('button');
199 extractButton.textContent = '📊 Export to CSV';
200 extractButton.style.cssText = `
201 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
202 color: white;
203 border: none;
204 padding: 8px 20px;
205 font-size: 14px;
206 font-weight: 600;
207 border-radius: 4px;
208 cursor: pointer;
209 transition: all 0.3s ease;
210 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
211 `;
212
213 // Create the Google Sheets export button
214 const sheetsButton = document.createElement('button');
215 sheetsButton.textContent = '📗 Export to Google Sheets';
216 sheetsButton.style.cssText = `
217 background: linear-gradient(135deg, #34a853 0%, #0f9d58 100%);
218 color: white;
219 border: none;
220 padding: 8px 20px;
221 font-size: 14px;
222 font-weight: 600;
223 border-radius: 4px;
224 cursor: pointer;
225 transition: all 0.3s ease;
226 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
227 `;
228
229 // Add hover effect for CSV button
230 extractButton.addEventListener('mouseenter', () => {
231 extractButton.style.transform = 'translateY(-2px)';
232 extractButton.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
233 });
234
235 extractButton.addEventListener('mouseleave', () => {
236 extractButton.style.transform = 'translateY(0)';
237 extractButton.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
238 });
239
240 // Add hover effect for Sheets button
241 sheetsButton.addEventListener('mouseenter', () => {
242 sheetsButton.style.transform = 'translateY(-2px)';
243 sheetsButton.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
244 });
245
246 sheetsButton.addEventListener('mouseleave', () => {
247 sheetsButton.style.transform = 'translateY(0)';
248 sheetsButton.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
249 });
250
251 // Add click handler for CSV export
252 extractButton.addEventListener('click', () => {
253 console.log('CSV Export button clicked');
254 extractButton.textContent = '⏳ Extracting...';
255 extractButton.disabled = true;
256
257 setTimeout(() => {
258 const products = extractProductData();
259
260 if (products.length === 0) {
261 alert('No products found on this page. Please make sure you are on a product listing page.');
262 extractButton.textContent = '📊 Export to CSV';
263 extractButton.disabled = false;
264 return;
265 }
266
267 const csvContent = convertToCSV(products);
268 const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
269 const filename = `dhgate-products-${timestamp}.csv`;
270
271 downloadCSV(csvContent, filename);
272
273 extractButton.textContent = `✅ Exported ${products.length} Products`;
274
275 setTimeout(() => {
276 extractButton.textContent = '📊 Export to CSV';
277 extractButton.disabled = false;
278 }, 2000);
279 }, 100);
280 });
281
282 // Add click handler for Google Sheets export
283 sheetsButton.addEventListener('click', () => {
284 console.log('Google Sheets Export button clicked');
285 sheetsButton.textContent = '⏳ Extracting...';
286 sheetsButton.disabled = true;
287
288 setTimeout(() => {
289 const products = extractProductData();
290
291 if (products.length === 0) {
292 alert('No products found on this page. Please make sure you are on a product listing page.');
293 sheetsButton.textContent = '📗 Export to Google Sheets';
294 sheetsButton.disabled = false;
295 return;
296 }
297
298 exportToGoogleSheets(products);
299
300 sheetsButton.textContent = `✅ Exported ${products.length} Products`;
301
302 setTimeout(() => {
303 sheetsButton.textContent = '📗 Export to Google Sheets';
304 sheetsButton.disabled = false;
305 }, 3000);
306 }, 100);
307 });
308
309 buttonContainer.appendChild(extractButton);
310 buttonContainer.appendChild(sheetsButton);
311
312 // Insert button next to the search area
313 const topSection = searchCard.querySelector('.mb-32.v-center.jc-sb');
314 if (topSection) {
315 const leftSection = topSection.querySelector('.v-center');
316 if (leftSection) {
317 leftSection.appendChild(buttonContainer);
318 console.log('Extraction button created successfully');
319 }
320 }
321 }
322
323 // Initialize the extension
324 function init() {
325 console.log('Initializing DHgate Affiliate Product Extractor...');
326
327 // Wait for the page to load
328 if (document.readyState === 'loading') {
329 document.addEventListener('DOMContentLoaded', init);
330 return;
331 }
332
333 // Try to create button immediately
334 createExtractionButton();
335
336 // Use MutationObserver to handle dynamic content loading
337 const observer = new MutationObserver(debounce(() => {
338 createExtractionButton();
339 }, 500));
340
341 observer.observe(document.body, {
342 childList: true,
343 subtree: true
344 });
345
346 // Also retry after a delay in case content loads slowly
347 setTimeout(createExtractionButton, 2000);
348 setTimeout(createExtractionButton, 4000);
349 }
350
351 // Start the extension
352 init();
353})();