ADVANCED people search on LinkedIn covering ALL the filters of the 'All filters' panel, ONE or MANY searches with AUTO-PAGINATION.
{"queries":"array of objects; each one requires page (pages optional, max 100). Rest of the filters optional: keywords, firstName, lastName, title, company, schoolText, network, geoUrn, currentCompany, pastCompany, school, industry, serviceCategory, profileLanguage, connectionOf, followerOf, openToVolunteer"}{"count":"int","results":"array of { query, pagesFetched, count, results:[{ name, degree, slug, profileUrl, headline, location }] }","skipped":"int (if applicable)"}ADVANCED people search on LinkedIn covering ALL the filters of the 'All filters' panel, ONE or MANY searches with AUTO-PAGINATION. Single PARAM: queries = array of objects. Each query accepts ALL these fields (all optional except page):
skipped). ~10 results per page. Returns { count, skipped?, results:[{ query, pagesFetched, count, results:[{ name, degree, slug, profileUrl, headline, location }] }] }. Requires a session. Regex parser over SSR HTML; if the markup or a param name changes, recapture from the 'All filters' panel.params. It does the in-page work and returns the JSON in returns.( async (root, params) => { let qs = params && params.queries; if (typeof qs === 'string') { try { qs = JSON.parse(qs); } catch(e){ qs = []; } } if (!Array.isArray(qs)) qs = []; const CONC = 20; const DELAY = 150; const MAXTASKS = 100; const enc = encodeURIComponent; const sleep = ms => new Promise(r=>setTimeout(r,ms)); const wrapArr = v => { if (v == null) return ''; if (Array.isArray(v)) v = v.join(','); v = String(v).trim(); if (!v) return ''; if (v.charAt(0) === '[') return v; return '[' + v.split(',').map(s => '"' + s.trim() + '"').join(',') + ']'; }; const buildUrl = (q, pg) => { const parts = ['keywords=' + enc(q.keywords || '')]; const arrKeys = { geoUrn:'geoUrn', currentCompany:'currentCompany', pastCompany:'pastCompany', school:'schoolFilter', industry:'industry', serviceCategory:'serviceCategory', profileLanguage:'profileLanguage', network:'network', followerOf:'followerOf' }; for (const k in arrKeys) { const w = wrapArr(q[k]); if (w) parts.push(arrKeys[k] + '=' + enc(w)); } const strKeys = { firstName:'firstName', lastName:'lastName', title:'title', company:'company', schoolText:'schoolFreetext', connectionOf:'connectionOf' }; for (const k in strKeys) { const v = (q[k] == null ? '' : String(q[k]).trim()); if (v) parts.push(strKeys[k] + '=%22' + enc(v) + '%22'); } if (q.openToVolunteer === true || q.openToVolunteer === 'true' || q.openToVolunteer === 1) parts.push('openToVolunteer=' + enc('["true"]')); parts.push('page=' + enc(String(pg))); parts.push('origin=FACETED_SEARCH'); return 'https://www.linkedin.com/search/results/people/?' + parts.join('&'); }; const parse = (html) => { const re=/href="https://www.linkedin.com/in/([^"]+)"\s+componentkey="[0-9a-f-]+"/g; const starts=[]; let m; while((m=re.exec(html))){ const slug=m[1].split(/[/?#]/)[0]; starts.push({slug, idx:m.index}); } const noise=/^(Connect|Follow|Send message|Message|View profile|Premium|Status)$/i; const out=[]; for(let k=0;k<starts.length;k++){ let chunk=html.slice(starts[k].idx, (starts[k+1]?starts[k+1].idx:starts[k].idx+2500)); chunk=chunk.slice(chunk.indexOf('>')+1); const lines=chunk.split(/<[^>]+>/).map(s=>s.replace(/&/g,'&').replace(/'/g,"'").replace(/"/g,'"').trim()).filter(Boolean); if(!lines.length) continue; let name=lines[0], degree=null; const dm=name.match(/•\s*(1er|2do|3er.|1st|2nd|3rd.|\d+º)/); if(dm){degree=dm[1];name=name.replace(/•./,'').trim();} const di=lines.findIndex(l=>/^•/.test(l)); if(!degree && di>=0) degree=lines[di].replace(/[•\s]/g,''); const rest=lines.slice(1).filter(l=>!noise.test(l) && !/^•/.test(l) && l.length>1 && l!==name); const headline=rest[0]||null; const location=rest.find(l=>/,| y alrededores|Argentina|Spain|Espa|United|Francia|Brasil|Chile|M[eé]xico|Egypt|Egipto/.test(l) && l!==headline) || null; out.push({slug:decodeURIComponent(starts[k].slug), name, degree, headline, location, profileUrl:'https://www.linkedin.com/in/'+starts[k].slug}); } return out; }; let tasks=[]; qs.forEach((q,qi)=>{ if (q.page===undefined || q.page===null || String(q.page).trim()==='') return; const start=parseInt(String(q.page),10)||1; let n=parseInt(String(q.pages||'1'),10)||1; if(n<1)n=1; if(n>100)n=100; for(let pg=start; pg<start+n; pg++) tasks.push({qi,pg,q}); }); let skipped=0; if(tasks.length>MAXTASKS){ skipped=tasks.length-MAXTASKS; tasks=tasks.slice(0,MAXTASKS); } const fetched=new Array(tasks.length); let next=0; const worker = async () => { while(true){ const i=next++; if(i>=tasks.length) break; const t=tasks[i]; if(DELAY>0) await sleep(DELAY0.5 + Math.random()*DELAY); try{ const r=await fetch(buildUrl(t.q,t.pg),{credentials:'include'}); const h=await r.text(); fetched[i]={qi:t.qi,pg:t.pg,people:parse(h)}; } catch(e){ fetched[i]={qi:t.qi,pg:t.pg,people:[],error:String(e)}; } } }; await Promise.all(Array.from({length:Math.min(CONC,tasks.length||1)}, worker)); const results=qs.map((q,qi)=>{ if (q.page===undefined || q.page===null || String(q.page).trim()==='') return {query:q,error:'page is required',count:0,results:[]}; const mine=fetched.filter(f=>f&&f.qi===qi).sort((a,b)=>a.pg-b.pg); const seen=new Set(); const merged=[]; mine.forEach(f=>f.people.forEach(p=>{ if(p.slug && !seen.has(p.slug)){seen.add(p.slug); merged.push(p);} })); return {query:q, pagesFetched:mine.length, count:merged.length, results:merged}; }); const ret={count:results.length, results}; if(skipped) ret.skipped=skipped; return ret; } )
Response text contains "LinkedIn".