- type: ask
param: targetProfileCount
message: >-
💡 This command works on a LinkedIn page that lists the company's employees.
Usually, you can find the link to these pages under the company name, where
the number of employees is shown.
How many LinkedIn profiles would you like to extract?
options:
- label: 👤 100
value: 100
- label: 👥 1k
value: 1000
- $custom
default: ''
vision:
enabled: false
mode: area
send: true
hint: ''
optionsInvalid: false
- type: js
code: |
async function scrollAndCollectProfiles(targetProfileCount) {
const config = {
scrollDelay: 2000,
maxRetries: 3,
selectors: {
nextButton: '.artdeco-pagination--has-controls .artdeco-button:nth-of-type(2)',
profileList: '[role="list"]',
profileContainer: 'li',
name: 'span[aria-hidden="true"]',
status: '.t-14.t-black.t-normal',
link: 'a[data-test-app-aware-link]'
}
};
const profiles = [];
const uniqueProfileLinks = new Set();
// Function to scroll down the page with a delay
const scrollAndWait = () => new Promise(resolve => {
window.scrollTo(0, document.body.scrollHeight);
setTimeout(resolve, config.scrollDelay);
});
// Function to extract profiles from the page
const extractCurrentProfiles = () => {
const containers = document.querySelectorAll(`${config.selectors.profileList} ${config.selectors.profileContainer}`);
let newProfilesFound = false;
containers.forEach(container => {
try {
const fullLink = container.querySelector(config.selectors.link).href;
const link = fullLink.split('?')[0];
if (!uniqueProfileLinks.has(link)) {
const name = container.querySelector(config.selectors.name)?.textContent.trim() || 'No name';
const status = container.querySelector(config.selectors.status)?.textContent.trim() || 'No status';
profiles.push({ name, status, link });
uniqueProfileLinks.add(link);
newProfilesFound = true;
}
} catch (error) {
console.log('Error processing profile:', error); // Logging errors
}
});
return newProfilesFound;
};
// Main loop to collect profiles until target is reached
let retries = 0;
while (profiles.length < targetProfileCount && retries < config.maxRetries) {
const newProfilesFound = extractCurrentProfiles();
retries = (profiles.length < targetProfileCount && !newProfilesFound) ? retries + 1 : 0;
await scrollAndWait();
const nextButton = document.querySelector(config.selectors.nextButton);
if (nextButton && !nextButton.disabled) {
nextButton.click();
await scrollAndWait();
} else if (!newProfilesFound) {
break; // Exit if no new profiles found
}
}
// Trim excess profiles if collected more than target count
profiles.length = Math.min(profiles.length, targetProfileCount); // Keep only up to target count
console.log(`Found ${profiles.length} profiles out of ${targetProfileCount} requested`);
return profiles; // Return the collected profiles
}
return scrollAndCollectProfiles(targetProfileCount);
param: json
timeout: 300000
args: targetProfileCount
silent: true
- type: say
message: |-
**Extracted:**
{{json}}
- param: action
options:
- label: ✅ DONE
value: done
- label: 📦 EXPORT JSON
value: export
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
message: ''
default: ''
optionsInvalid: false
- type: stop
condition: '{{action}} = done'
- what: param
filename: linkedin-group-members.txt
condition: '{{action}} = export'
type: export
param: json
All rights reserved © HARPA AI TECHNOLOGIES LLC, 2021 — 2025
Designed and engineered in Finland 🇫🇮