DiscoverProfile Scraper
JavaScriptPublicby zx-apiAug 18, 2026, 12:54 PMExpires: Never164 views
1/**
2 * DiscoverProfile Scraper
3 * Scraper untuk mencari profil media sosial berdasarkan username/nickname
4 * Sumber: https://whatsapp.com/channel/0029VbDLqe7EquiSF4STU13o/324
5 * Credit By Zx
6 * Note! Kalau mau di sher harap jangan hapus credit atau sumber saluran ๐ฅถ
7 */
8
9const axios = require('axios');
10
11class DiscoverProfile {
12 constructor(options = {}) {
13 this.baseURL = options.baseURL || 'https://api.discoverprofile.com';
14 this.timeout = options.timeout || 30000;
15 this.retries = options.retries || 3;
16 this.retryDelay = options.retryDelay || 1000;
17
18 this.client = axios.create({
19 baseURL: this.baseURL,
20 timeout: this.timeout,
21 headers: {
22 'Accept': 'application/json, text/plain, */*',
23 'Content-Type': 'application/json',
24 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Mobile Safari/537.36',
25 'Referer': 'https://discoverprofile.com/',
26 'Origin': 'https://discoverprofile.com',
27 ...options.headers
28 }
29 });
30
31 // Cache untuk menyimpan hasil pencarian
32 this._cache = new Map();
33 this._cacheTTL = options.cacheTTL || 5 * 60 * 1000; // 5 menit default
34 }
35
36 /**
37 * Cari profil berdasarkan username/nickname
38 * @param {string} source - Username/nickname yang dicari
39 * @param {Object} [options={}] - Opsi pencarian
40 * @param {string} [options.type='name'] - Tipe pencarian: 'name' atau 'email'
41 * @param {boolean} [options.rescan=false] - Force rescan
42 * @returns {Promise<Object>} Hasil pencarian
43 */
44 async search(source, options = {}) {
45 const {
46 type = 'name',
47 rescan = false
48 } = options;
49
50 if (!source || typeof source !== 'string') {
51 throw new Error('Parameter "source" harus berupa string yang valid');
52 }
53
54 const cacheKey = `${source}:${type}:${rescan}`;
55
56 // Cek cache jika bukan rescan
57 if (!rescan && this._cache.has(cacheKey)) {
58 const cached = this._cache.get(cacheKey);
59 if (Date.now() - cached.timestamp < this._cacheTTL) {
60 return cached.data;
61 }
62 this._cache.delete(cacheKey);
63 }
64
65 const payload = {
66 source: source.trim(),
67 type,
68 rescan
69 };
70
71 const result = await this._requestWithRetry('/discoverprofile', 'POST', payload);
72
73 // Simpan ke cache
74 this._cache.set(cacheKey, {
75 data: result,
76 timestamp: Date.now()
77 });
78
79 return result;
80 }
81
82 /**
83 * Cari profil berdasarkan username (alias untuk search dengan type='name')
84 * @param {string} username - Username yang dicari
85 * @returns {Promise<Object>}
86 */
87 async searchByUsername(username) {
88 return this.search(username, { type: 'name' });
89 }
90
91 /**
92 * Cari profil berdasarkan email
93 * @param {string} email - Email yang dicari
94 * @returns {Promise<Object>}
95 */
96 async searchByEmail(email) {
97 return this.search(email, { type: 'email' });
98 }
99
100 /**
101 * Filter hasil berdasarkan kategori
102 * @param {Object} searchResult - Hasil dari search()
103 * @param {string} category - Kategori yang difilter
104 * @returns {Array} Daftar profil sesuai kategori
105 */
106 filterByCategory(searchResult, category) {
107 if (!searchResult?.result) return [];
108 return searchResult.result.filter(
109 item => item.category?.toLowerCase() === category.toLowerCase()
110 );
111 }
112
113 /**
114 * Filter hasil hanya yang eksis (isExist = true)
115 * @param {Object} searchResult - Hasil dari search()
116 * @returns {Array} Daftar profil yang ditemukan
117 */
118 filterExisting(searchResult) {
119 if (!searchResult?.result) return [];
120 return searchResult.result.filter(item => item.isExist === true);
121 }
122
123 /**
124 * Filter hasil yang tidak eksis
125 * @param {Object} searchResult - Hasil dari search()
126 * @returns {Array} Daftar profil yang tidak ditemukan
127 */
128 filterNonExisting(searchResult) {
129 if (!searchResult?.result) return [];
130 return searchResult.result.filter(item => item.isExist === false);
131 }
132
133 /**
134 * Dapatkan semua kategori unik dari hasil
135 * @param {Object} searchResult - Hasil dari search()
136 * @returns {Array<string>} Daftar kategori unik
137 */
138 getCategories(searchResult) {
139 if (!searchResult?.result) return [];
140 const categories = new Set(
141 searchResult.result.map(item => item.category).filter(Boolean)
142 );
143 return [...categories].sort();
144 }
145
146 /**
147 * Dapatkan ringkasan statistik hasil pencarian
148 * @param {Object} searchResult - Hasil dari search()
149 * @returns {Object} Statistik
150 */
151 getStats(searchResult) {
152 const results = searchResult?.result || [];
153 const existing = results.filter(r => r.isExist);
154 const categories = this.getCategories(searchResult);
155
156 const categoryStats = {};
157 for (const cat of categories) {
158 const catItems = results.filter(r => r.category === cat);
159 const catExisting = catItems.filter(r => r.isExist);
160 categoryStats[cat] = {
161 total: catItems.length,
162 found: catExisting.length,
163 notFound: catItems.length - catExisting.length
164 };
165 }
166
167 return {
168 totalChecked: results.length,
169 totalFound: existing.length,
170 totalNotFound: results.length - existing.length,
171 totalCategories: categories.length,
172 categories: categoryStats,
173 createdAt: searchResult?.createdAt || null
174 };
175 }
176
177 /**
178 * Dapatkan semua platform yang tersedia dari hasil
179 * @param {Object} searchResult - Hasil dari search()
180 * @returns {Array<string>} Daftar nama platform
181 */
182 getPlatforms(searchResult) {
183 if (!searchResult?.result) return [];
184 return searchResult.result.map(item => item.source).filter(Boolean);
185 }
186
187 /**
188 * Cari di platform tertentu dari hasil
189 * @param {Object} searchResult - Hasil dari search()
190 * @param {string} platformName - Nama platform (case-insensitive)
191 * @returns {Object|null} Profil di platform tersebut
192 */
193 findByPlatform(searchResult, platformName) {
194 if (!searchResult?.result) return null;
195 return searchResult.result.find(
196 item => item.source?.toLowerCase() === platformName.toLowerCase()
197 ) || null;
198 }
199
200 /**
201 * Batch search beberapa username sekaligus
202 * @param {Array<string>} usernames - Daftar username
203 * @param {Object} [options={}] - Opsi
204 * @param {number} [options.concurrency=3] - Jumlah request paralel
205 * @param {number} [options.delay=500] - Delay antar batch dalam ms
206 * @returns {Promise<Object>} Map username -> hasil
207 */
208 async batchSearch(usernames, options = {}) {
209 const { concurrency = 3, delay = 500 } = options;
210 const results = {};
211
212 for (let i = 0; i < usernames.length; i += concurrency) {
213 const batch = usernames.slice(i, i + concurrency);
214
215 const batchPromises = batch.map(async (username) => {
216 try {
217 const result = await this.search(username);
218 results[username] = { success: true, data: result };
219 } catch (error) {
220 results[username] = { success: false, error: error.message };
221 }
222 });
223
224 await Promise.all(batchPromises);
225
226 if (i + concurrency < usernames.length) {
227 await this._sleep(delay);
228 }
229 }
230
231 return results;
232 }
233
234 clearCache() {
235 this._cache.clear();
236 }
237
238 get cacheSize() {
239 return this._cache.size;
240 }
241
242 async _requestWithRetry(endpoint, method = 'GET', data = null, attempt = 1) {
243 try {
244 const config = { method, url: endpoint };
245 if (data && (method === 'POST' || method === 'PUT')) {
246 config.data = data;
247 }
248
249 const response = await this.client.request(config);
250
251 if (response.data?.error) {
252 throw new Error(`API Error: ${response.data.error}`);
253 }
254
255 return response.data;
256 } catch (error) {
257 if (attempt < this.retries) {
258 const delay = this.retryDelay * attempt;
259 await this._sleep(delay);
260 return this._requestWithRetry(endpoint, method, data, attempt + 1);
261 }
262
263 throw new Error(
264 `Request gagal setelah ${this.retries} percobaan: ${error.message}`
265 );
266 }
267 }
268
269 _sleep(ms) {
270 return new Promise(resolve => setTimeout(resolve, ms));
271 }
272
273 _escapeCSV(value) {
274 if (typeof value !== 'string') return String(value || '');
275 if (value.includes(',') || value.includes('"') || value.includes('\n')) {
276 return `"${value.replace(/"/g, '""')}"`;
277 }
278 return value;
279 }
280}
281
282// ==================== EXPORT ====================
283module.exports = DiscoverProfile;
284
285// ==================== CONTOH PENGGUNAAN ====================
286if (require.main === module) {
287 (async () => {
288 const scraper = new DiscoverProfile({
289 timeout: 30000,
290 retries: 3,
291 retryDelay: 1000
292 });
293
294 try {
295 // Cari username
296 console.log('๐ Mencari username "jokowi"...');
297 const result = await scraper.searchByUsername('jokowi');
298
299 // Statistik
300 const stats = scraper.getStats(result);
301 console.log('\n๐ Statistik:');
302 console.log(` Total platform dicek: ${stats.totalChecked}`);
303 console.log(` Ditemukan: ${stats.totalFound}`);
304 console.log(` Tidak ditemukan: ${stats.totalNotFound}`);
305 console.log(` Total kategori: ${stats.totalCategories}`);
306
307 // Hanya yang eksis
308 const existing = scraper.filterExisting(result);
309 console.log(`\nโ
Profil yang ditemukan (${existing.length}):`);
310 existing.forEach(item => {
311 console.log(` โข ${item.source}: ${item.url} [${item.category}]`);
312 });
313
314 // Filter per kategori
315 const socialMedia = scraper.filterByCategory(result, 'Social network');
316 console.log(`\n๐ Social Networks (${socialMedia.length}):`);
317 socialMedia.forEach(item => {
318 const status = item.isExist ? 'โ
' : 'โ';
319 console.log(` ${status} ${item.source}: ${item.url}`);
320 });
321
322 // Cari platform spesifik
323 const instagram = scraper.findByPlatform(result, 'Instagram');
324 if (instagram) {
325 console.log(`\n๐ท Instagram: ${instagram.url} (${instagram.isExist ? 'Found' : 'Not Found'})`);
326 }
327
328 } catch (error) {
329 console.error('โ Error:', error.message);
330 }
331 })();
332}332 linesยท10,160 charsยท9.9 KB
WwrapยทFfullscreen