Company Email Extractor for MyLimoBiz

Extract email addresses from company profiles across multiple pages

Size

14.5 KB

Version

1.1.1

Created

Dec 9, 2025

Updated

7 days ago

1// ==UserScript==
2// @name		Company Email Extractor for MyLimoBiz
3// @description		Extract email addresses from company profiles across multiple pages
4// @version		1.1.1
5// @match		https://*.manage.mylimobiz.com/*
6// @icon		https://manage.mylimobiz.com/_images/favicons/favicon-32x32.png
7// @grant		GM.getValue
8// @grant		GM.setValue
9// @grant		GM.xmlhttpRequest
10// ==/UserScript==
11(function() {
12    'use strict';
13
14    // Utility function to delay execution
15    const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
16
17    // Debounce function for MutationObserver
18    function debounce(func, wait) {
19        let timeout;
20        return function executedFunction(...args) {
21            const later = () => {
22                clearTimeout(timeout);
23                func(...args);
24            };
25            clearTimeout(timeout);
26            timeout = setTimeout(later, wait);
27        };
28    }
29
30    // Extract emails from a profile page HTML
31    function extractEmailsFromProfile(html, companyName, profileUrl) {
32        const parser = new DOMParser();
33        const doc = parser.parseFromString(html, 'text/html');
34        
35        const emails = [];
36        const emailLinks = doc.querySelectorAll('a[href^="mailto:"]');
37        
38        emailLinks.forEach(link => {
39            const email = link.href.replace('mailto:', '');
40            const section = link.closest('section');
41            let contactType = 'Unknown';
42            
43            if (section) {
44                const header = section.querySelector('h5');
45                if (header) {
46                    contactType = header.textContent.trim();
47                }
48            }
49            
50            emails.push({
51                email: email,
52                contactType: contactType
53            });
54        });
55        
56        return {
57            companyName: companyName,
58            profileUrl: profileUrl,
59            emails: emails
60        };
61    }
62
63    // Fetch a page and extract company links
64    async function fetchPageCompanies(pageNumber) {
65        console.log(`Fetching page ${pageNumber}...`);
66        
67        const url = `https://manage.mylimobiz.com/adminnew/LaNet/SearchAffiliates/US?PageIndex=${pageNumber - 1}&CompanyName=&state=&AirportsServiced=&MarketsServiced=&LaBlackChecked=false`;
68        
69        return new Promise((resolve, reject) => {
70            GM.xmlhttpRequest({
71                method: 'GET',
72                url: url,
73                anonymous: false,
74                onload: function(response) {
75                    console.log(`Response received for page ${pageNumber}, status: ${response.status}`);
76                    
77                    const parser = new DOMParser();
78                    const doc = parser.parseFromString(response.responseText, 'text/html');
79                    
80                    const companies = [];
81                    const rows = doc.querySelectorAll('table.table tbody tr');
82                    
83                    console.log(`Found ${rows.length} rows on page ${pageNumber}`);
84                    
85                    rows.forEach(row => {
86                        const nameCell = row.querySelector('td:first-child');
87                        const linkCell = row.querySelector('td:last-child a[href*="/AffiliateProfile/"]');
88                        
89                        if (nameCell && linkCell) {
90                            const companyName = nameCell.textContent.trim();
91                            const profileUrl = 'https://manage.mylimobiz.com' + linkCell.getAttribute('href');
92                            
93                            console.log(`Found company: ${companyName}`);
94                            
95                            companies.push({
96                                name: companyName,
97                                url: profileUrl
98                            });
99                        }
100                    });
101                    
102                    console.log(`Extracted ${companies.length} companies from page ${pageNumber}`);
103                    resolve(companies);
104                },
105                onerror: function(error) {
106                    console.error(`Error fetching page ${pageNumber}:`, error);
107                    reject(error);
108                }
109            });
110        });
111    }
112
113    // Fetch a company profile and extract emails
114    async function fetchCompanyProfile(company) {
115        console.log(`Fetching profile for: ${company.name}`);
116        
117        return new Promise((resolve, reject) => {
118            GM.xmlhttpRequest({
119                method: 'GET',
120                url: company.url,
121                anonymous: false,
122                onload: function(response) {
123                    const profileData = extractEmailsFromProfile(response.responseText, company.name, company.url);
124                    resolve(profileData);
125                },
126                onerror: function(error) {
127                    console.error(`Error fetching profile for ${company.name}:`, error);
128                    reject(error);
129                }
130            });
131        });
132    }
133
134    // Main extraction function
135    async function extractAllEmails(startPage, endPage, progressCallback) {
136        const allResults = [];
137        let totalCompanies = 0;
138        let processedCompanies = 0;
139
140        try {
141            // First, collect all companies from all pages
142            console.log(`Collecting companies from pages ${startPage} to ${endPage}...`);
143            const allCompanies = [];
144            
145            for (let page = startPage; page <= endPage; page++) {
146                try {
147                    const companies = await fetchPageCompanies(page);
148                    allCompanies.push(...companies);
149                    totalCompanies = allCompanies.length;
150                    
151                    if (progressCallback) {
152                        progressCallback({
153                            phase: 'collecting',
154                            currentPage: page,
155                            totalPages: endPage - startPage + 1,
156                            totalCompanies: totalCompanies
157                        });
158                    }
159                    
160                    await delay(500); // Small delay between page requests
161                } catch (error) {
162                    console.error(`Failed to fetch page ${page}:`, error);
163                }
164            }
165
166            console.log(`Found ${totalCompanies} companies. Now extracting emails...`);
167
168            // Now fetch each company profile
169            for (const company of allCompanies) {
170                try {
171                    const profileData = await fetchCompanyProfile(company);
172                    allResults.push(profileData);
173                    processedCompanies++;
174                    
175                    if (progressCallback) {
176                        progressCallback({
177                            phase: 'extracting',
178                            processedCompanies: processedCompanies,
179                            totalCompanies: totalCompanies,
180                            currentCompany: company.name
181                        });
182                    }
183                    
184                    await delay(300); // Small delay between profile requests
185                } catch (error) {
186                    console.error(`Failed to fetch profile for ${company.name}:`, error);
187                }
188            }
189
190            // Save results
191            await GM.setValue('extractedEmails', JSON.stringify(allResults));
192            console.log('Extraction complete!', allResults);
193            
194            return allResults;
195        } catch (error) {
196            console.error('Error during extraction:', error);
197            throw error;
198        }
199    }
200
201    // Convert results to CSV
202    function convertToCSV(results) {
203        const rows = [['Company Name', 'Contact Type', 'Email Address', 'Profile URL']];
204        
205        results.forEach(company => {
206            if (company.emails.length === 0) {
207                rows.push([company.companyName, 'No email found', '', company.profileUrl]);
208            } else {
209                company.emails.forEach(emailData => {
210                    rows.push([
211                        company.companyName,
212                        emailData.contactType,
213                        emailData.email,
214                        company.profileUrl
215                    ]);
216                });
217            }
218        });
219        
220        return rows.map(row => row.map(cell => `"${cell}"`).join(',')).join('\n');
221    }
222
223    // Download CSV file
224    function downloadCSV(csv, filename) {
225        const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
226        const link = document.createElement('a');
227        const url = URL.createObjectURL(blob);
228        
229        link.setAttribute('href', url);
230        link.setAttribute('download', filename);
231        link.style.visibility = 'hidden';
232        document.body.appendChild(link);
233        link.click();
234        document.body.removeChild(link);
235    }
236
237    // Create UI
238    function createUI() {
239        // Check if we're on the search page
240        if (!window.location.href.includes('/SearchAffiliates/')) {
241            return;
242        }
243
244        // Create container
245        const container = document.createElement('div');
246        container.id = 'email-extractor-ui';
247        container.style.cssText = `
248            position: fixed;
249            top: 20px;
250            right: 20px;
251            background: white;
252            border: 2px solid #007bff;
253            border-radius: 8px;
254            padding: 20px;
255            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
256            z-index: 10000;
257            min-width: 350px;
258            font-family: Arial, sans-serif;
259        `;
260
261        container.innerHTML = `
262            <h3 style="margin: 0 0 15px 0; color: #333; font-size: 18px;">Email Extractor</h3>
263            <div style="margin-bottom: 15px;">
264                <label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">Start Page:</label>
265                <input type="number" id="start-page" value="119" min="1" max="217" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
266            </div>
267            <div style="margin-bottom: 15px;">
268                <label style="display: block; margin-bottom: 5px; font-weight: bold; color: #555;">End Page:</label>
269                <input type="number" id="end-page" value="217" min="1" max="217" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
270            </div>
271            <button id="start-extraction" style="width: 100%; padding: 10px; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; font-weight: bold; margin-bottom: 10px;">
272                Start Extraction
273            </button>
274            <button id="download-csv" style="width: 100%; padding: 10px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; font-weight: bold; display: none;">
275                Download CSV
276            </button>
277            <div id="progress-info" style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 4px; display: none;">
278                <div style="font-weight: bold; color: #333; margin-bottom: 5px;">Status:</div>
279                <div id="progress-text" style="color: #666; font-size: 14px;"></div>
280            </div>
281        `;
282
283        document.body.appendChild(container);
284
285        // Add event listeners
286        const startBtn = document.getElementById('start-extraction');
287        const downloadBtn = document.getElementById('download-csv');
288        const progressInfo = document.getElementById('progress-info');
289        const progressText = document.getElementById('progress-text');
290
291        let extractedData = null;
292
293        startBtn.addEventListener('click', async () => {
294            const startPage = parseInt(document.getElementById('start-page').value);
295            const endPage = parseInt(document.getElementById('end-page').value);
296
297            if (startPage < 1 || startPage > 217 || endPage < 1 || endPage > 217 || startPage > endPage) {
298                alert('Please enter valid page numbers (1-217)');
299                return;
300            }
301
302            startBtn.disabled = true;
303            startBtn.style.background = '#6c757d';
304            startBtn.textContent = 'Extracting...';
305            progressInfo.style.display = 'block';
306            downloadBtn.style.display = 'none';
307
308            try {
309                extractedData = await extractAllEmails(startPage, endPage, (progress) => {
310                    if (progress.phase === 'collecting') {
311                        progressText.innerHTML = `
312                            <strong>Phase 1:</strong> Collecting companies<br>
313                            Page: ${progress.currentPage} / ${progress.totalPages}<br>
314                            Companies found: ${progress.totalCompanies}
315                        `;
316                    } else if (progress.phase === 'extracting') {
317                        progressText.innerHTML = `
318                            <strong>Phase 2:</strong> Extracting emails<br>
319                            Progress: ${progress.processedCompanies} / ${progress.totalCompanies}<br>
320                            Current: ${progress.currentCompany}
321                        `;
322                    }
323                });
324
325                progressText.innerHTML = `
326                    <strong style="color: #28a745;">✓ Complete!</strong><br>
327                    Extracted emails from ${extractedData.length} companies
328                `;
329                
330                downloadBtn.style.display = 'block';
331                startBtn.textContent = 'Start Extraction';
332                startBtn.style.background = '#28a745';
333                startBtn.disabled = false;
334
335            } catch (error) {
336                progressText.innerHTML = `<strong style="color: #dc3545;">Error:</strong> ${error.message}`;
337                startBtn.textContent = 'Start Extraction';
338                startBtn.style.background = '#28a745';
339                startBtn.disabled = false;
340            }
341        });
342
343        downloadBtn.addEventListener('click', () => {
344            if (extractedData) {
345                const csv = convertToCSV(extractedData);
346                const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
347                downloadCSV(csv, `company-emails-${timestamp}.csv`);
348            }
349        });
350    }
351
352    // Initialize
353    function init() {
354        if (document.readyState === 'loading') {
355            document.addEventListener('DOMContentLoaded', createUI);
356        } else {
357            createUI();
358        }
359    }
360
361    init();
362})();
Company Email Extractor for MyLimoBiz | Robomonkey