Auto Connect Bot for LinkedIn
Building professional networks on LinkedIn can be a repetitive and time-consuming task. In this project, you will build a Python automation bot that navigates your LinkedIn suggestions, automatically sends connection requests, and includes a personalized message for each user.
To build this tool, you will use Selenium WebDriver for browser automation and BeautifulSoup for parsing HTML structures.
Prerequisites and Setup
Before writing the script, install the required libraries by running the following commands:
pip install selenium beautifulsoup4
For more documentation on these libraries, refer to the following resources:
In addition to these packages, you must download a web driver matching your browser. For Google Chrome, retrieve the appropriate binary from the ChromeDriver Downloads page and save it directly in your project root directory.
Finally, create a file named config.txt in your project root to hold your credentials. The file must contain your login email on the first line and your password on the second line:
your_linkedin_email@example.com
your_secure_password
For a step-by-step walkthrough of the code and execution, watch the video tutorial below:
The following imports bring in the necessary libraries.
import os, random, sys, time
from selenium import webdriver
from bs4 import BeautifulSoup
The line below fetches the address of the Google Chrome driver. After running it, a new Google Chrome window opens. Since no link is passed, the window starts blank.
browser = webdriver.Chrome('driver/chromedriver.exe')
Open the LinkedIn login page using browser.get().
browser.get('https://www.linkedin.com/uas/login')
Open the config.txt file and read the username and password from it.
file = open('config.txt')
lines = file.readlines()
username = lines[0]
password = lines[1]
To automate the login process, check the id of the textboxes that accept the username and password on the webpage. Right-click anywhere on the webpage and click 'inspect'. The id of the username textbox is username and the id of the password textbox is password.
find_element_by_id() returns the first element with the id attribute value matching the location. The send_keys() method sends text to any field, such as an input field or anchor tag paragraph. It replaces the existing contents in the browser. submit() submits a form after data has been sent to it.
Once you pass the username and password, you can see them on the webpage before submitting.
Note:- The IDs of the textboxes can change. Hence before running this code check the current ID of the textboxes by inspecting the webpage.
elementID = browser.find_element_by_id('username')
elementID.send_keys(username)
elementID = browser.find_element_by_id('password')
elementID.send_keys(password)
elementID.submit()
To visit different profiles, construct the full URL by appending each profile ID to the base URL https://www.linkedin.com. The code below opens the profile whose ID is /in/aarya-tadvalkar-092650193/.
visitingProfileID = '/in/aarya-tadvalkar-092650193/'
fullLink = 'https://www.linkedin.com' + visitingProfileID
browser.get(fullLink)
The function below returns a list of profile IDs suggested by LinkedIn. It receives the entire page source, parsed into a BeautifulSoup object via browser.page_source. It finds the div with class pv-browsemap-section, then locates all a tags with class pv-browsemap-section__member. The href of each tag holds a profile ID. Any ID not already in profilesQueued or visitedProfiles is added to profilesID, which the function returns.
Note:- The class names used here can change. Hence before running this code check the current class name by inspecting the webpage.
visitedProfiles = []
profilesQueued = []
def getNewProfileIDs(soup, profilesQueued):
profilesID = []
pav = soup.find('div', {'class': 'pv-browsemap-section'})
all_links = pav.findAll('a', {'class': 'pv-browsemap-section__member'})
for link in all_links:
userID = link.get('href')
if((userID not in profilesQueued) and (userID not in visitedProfiles)):
profilesID.append(userID)
return profilesID
getNewProfileIDs(BeautifulSoup(browser.page_source), profilesQueued)
['/in/saransh-kotha-567a84182/', '/in/sonali-bedade-0519071ab/', '/in/shreya-mali-1a6456191/', '/in/advait-raut-6a060616b/', '/in/swati-tiwari12/', '/in/srishti-s-agrawal/', '/in/preyashgothane/', '/in/mohini-chaudhari-b77a74155/', '/in/ketan-mankar/', '/in/shubhra-masurkar-940a3a1a4/']
profilesQueued = getNewProfileIDs(BeautifulSoup(browser.page_source), profilesQueued)
For each link in profilesQueued, the script performs the following actions:
- Appends the profile ID to the base URL
https://www.linkedin.comto formfullLink, then navigates to it. - Finds the connect button by its class name
pv-s-profile-actionsand clicks it. - After clicking connect, a prompt appears asking whether to add a note. Click the "Add a note" button by its class name
mr1. - Select the text area using its id
custom-messageand send the personalized message usingsend_keys(). - Click the done button, which has class
ml1. - Write the current profile ID to
visitedUsers.txt. - Fetch suggested profiles from the current profile using
getNewProfileIDs()and extendprofilesQueuedwith the new IDs. - Introduce a random delay to mimic human browsing behavior and avoid rate limits.
while profilesQueued:
try:
visitingProfileID = profilesQueued.pop()
visitedProfiles.append(visitingProfileID)
fullLink = 'https://www.linkedin.com' + visitingProfileID
browser.get(fullLink)
browser.find_element_by_class_name('pv-s-profile-actions').click()
browser.find_element_by_class_name('mr1').click()
customMessage = "Hello, I have found mutual interest area and I would be more than happy to connect with you. Kindly, accept my invitation. Thanks!"
elementID = browser.find_element_by_id('custom-message')
elementID.send_keys(customMessage)
browser.find_element_by_class_name('ml1').click()
# Add the ID to the visitedUsersFile
with open('visitedUsers.txt', 'a') as visitedUsersFile:
visitedUsersFile.write(str(visitingProfileID)+'\n')
visitedUsersFile.close()
# Get new profiles ID
soup = BeautifulSoup(browser.page_source)
try:
profilesQueued.extend(getNewProfileIDs(soup, profilesQueued))
except:
print('Continue')
# Pause
time.sleep(random.uniform(3, 7)) # Otherwise, sleep to make sure everything loads
if(len(visitedProfiles)%50==0):
print('Visited Profiles: ', len(visitedProfiles))
if(len(profilesQueued)>100000):
with open('profilesQueued.txt', 'a') as visitedUsersFile:
visitedUsersFile.write(str(visitingProfileID)+'\n')
visitedUsersFile.close()
print('100,000 Done!!!')
break;
except:
print('error')
Conclusion
In this tutorial, you built a fully functional LinkedIn automation bot using Selenium and BeautifulSoup. The script automates the login process, traverses suggestion feeds, and programmatically submits personalized invitation notes while recording visited profiles to prevent duplicates.
Key takeaways:
- Selenium simulates actual browser events (like clicks and text input), bypassing static scraping limitations.
- Storing credentials in an external
config.txtfile keeps code clean and modular. - Parsing the dynamic DOM structure with BeautifulSoup allows you to retrieve relative URLs for prospective connections.
- Adding randomized delay offsets with Python's
time.sleep()is important to mimic human browsing behavior and avoid account rate limits.
Next steps:
- Read the LinkedIn Profile Scraper tutorial to learn how to extract professional histories, skills, and titles from public profiles.
- Try customizing the personalized connection note dynamically by extracting the recipient's first name from their profile page before sending the request.
- Add error logging to output failed connection attempts to a separate log file for debugging.