From 2e1df70eafe6ccd60d360b0b465ba11be44b5209 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 5 Jun 2026 22:54:49 +0300 Subject: [PATCH] Auto-deploy: 2026-06-05 22:54:49 --- search_analyzer.js | 132 ++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 81 deletions(-) diff --git a/search_analyzer.js b/search_analyzer.js index 9927319..a39fc55 100644 --- a/search_analyzer.js +++ b/search_analyzer.js @@ -44,50 +44,36 @@ }; try { - // 1. Extract Name (Prioritize links over generic spans) - const profileLinks = Array.from(cardEl.querySelectorAll('a[href*="/in/"]')) - .filter(a => a.innerText.trim().length > 2 && !a.innerText.includes('LinkedIn')); + // The most robust way to extract data on LinkedIn is reading the visible text lines. + // Profile cards always follow: Name -> Degree -> Headline -> Location -> ... + const rawLines = cardEl.innerText.split('\n').map(s => s.trim()).filter(s => s.length > 1); - if (profileLinks.length > 0) { - data.name = profileLinks[0].innerText.trim().split('\n')[0]; - } else { - const nameEl = cardEl.querySelector('.entity-result__title-text, .search-result__title, span[dir="ltr"]'); - if (nameEl) data.name = nameEl.innerText.trim().split('\n')[0]; + // Remove lines that are just action buttons or connection degrees + const lines = rawLines.filter(s => { + const low = s.toLowerCase(); + return !low.includes('degree connection') && + !['1st', '2nd', '3rd', '3rd+'].includes(low) && + !low.includes('connect') && + !low.includes('message') && + !low.includes('pending') && + !low.includes('follow') && + !low.includes('view profile'); + }); + + if (lines.length > 0) data.name = lines[0]; + if (lines.length > 1) data.headline = lines[1]; + if (lines.length > 2) data.location = lines[2]; + + // Summary is everything else + if (lines.length > 3) { + data.summary = lines.slice(3, 8).join(' | '); } - // 2. Extract Headline - const headlineEl = cardEl.querySelector('.entity-result__primary-subtitle, [class*="subtitle"], .linked-area'); - if (headlineEl) { - data.headline = headlineEl.innerText.trim(); - } - - // 3. Extract Location - const locationEl = cardEl.querySelector('.entity-result__secondary-subtitle, .search-result__info'); - if (locationEl) { - data.location = locationEl.innerText.trim(); - } - - // 4. Extract Summary - const summaryEl = cardEl.querySelector('.entity-result__summary, .search-result__snippets'); - if (summaryEl) { - data.summary = summaryEl.innerText.trim(); - } - - // Clean up - if (data.name) data.name = data.name.replace(/View .* profile/gi, '').trim(); - - // Ultimate Fallback: just take the first lines of text in the card - if (!data.name || data.name.length < 2) { - const lines = cardEl.innerText.split('\n').map(s => s.trim()).filter(s => s.length > 2 && !s.includes('Degree connection')); - if (lines.length > 0) data.name = lines[0]; - if (lines.length > 1 && !data.headline) data.headline = lines[1]; - } - - // Final fallback to avoid empty name - if (!data.name) { + // Cleanup name if it accidentally caught something weird + if (data.name && data.name.includes('LinkedIn')) { data.name = 'مستثمر محتمل'; - data.summary = cardEl.innerText ? cardEl.innerText.substring(0, 300) : ''; } + } catch (e) { console.error('[LJA] Extraction failed', e); data.name = 'مستثمر محتمل'; @@ -101,52 +87,36 @@ function findCards() { let cards = []; - // Method 1: Try common structural wrappers for search results - let listItems = Array.from(document.querySelectorAll('ul > li')); - let validListItems = listItems.filter(li => li.innerText.length > 20 && li.querySelector('img')); - if (validListItems.length > 0) { - cards = validListItems; - console.log('[LJA] Found cards via ul > li with images:', cards.length); - } + // LinkedIn search results are usually list items with an image and an action button + let listItems = Array.from(document.querySelectorAll('li, .reusable-search__result-container, .search-entity')); - // Method 2: Try finding elements containing headlines - if (cards.length === 0) { - let subtitles = document.querySelectorAll('.entity-result__primary-subtitle, [class*="subtitle"], .linked-area'); - let validSubtitles = Array.from(subtitles).filter(s => s.innerText.trim().length > 5); - if (validSubtitles.length > 0) { - let uniqueCards = new Set(); - validSubtitles.forEach(s => { - let container = s.closest('li') || s.closest('div[data-chameleon-result-urn]') || s.parentElement.parentElement.parentElement; - if (container) uniqueCards.add(container); - }); - cards = Array.from(uniqueCards); - console.log('[LJA] Found cards via subtitles:', cards.length); + // Filter to only those that look like profile cards + let validCards = listItems.filter(el => { + const txt = el.innerText.toLowerCase(); + // Must have significant text, an image, and a typical LinkedIn action button + const hasAction = txt.includes('connect') || txt.includes('message') || txt.includes('pending') || txt.includes('follow'); + const hasImage = el.querySelector('img'); + const isNotSidebar = !txt.includes('about') && !txt.includes('accessibility'); + return txt.length > 50 && hasAction && hasImage && isNotSidebar; + }); + + if (validCards.length > 0) { + cards = validCards; + console.log('[LJA] Found cards via strict heuristics:', cards.length); + } + + // Deduplicate cards by their text content to avoid nested matches + const uniqueCards = []; + const seenText = new Set(); + for (const card of cards) { + const txt = card.innerText.trim().substring(0, 100); // use first 100 chars as signature + if (!seenText.has(txt)) { + seenText.add(txt); + uniqueCards.push(card); } } - // Method 3: Deep heuristic using profile links - if (cards.length === 0) { - let profileLinks = Array.from(document.querySelectorAll('a[href*="/in/"]')).filter(a => a.innerText.trim().length > 3 && !a.querySelector('img')); - console.log('[LJA] Profile links found:', profileLinks.length); - - let uniqueCards = new Set(); - profileLinks.forEach(link => { - let container = link.parentElement; - let bestContainer = null; - for(let i = 0; i < 8; i++) { - if (!container) break; - if (container.innerText.length > 50 && container.querySelectorAll('button').length > 0) { - bestContainer = container; - } - container = container.parentElement; - } - if (bestContainer) uniqueCards.add(bestContainer); - }); - cards = Array.from(uniqueCards); - console.log('[LJA] Found cards via link heuristics:', cards.length); - } - - return Array.from(new Set(cards)); + return uniqueCards; } // ─── Inject UI into Card ─────────────────────────────────────────────────