LinkedIn Auto Connect Bot

Build a LinkedIn automation bot in Python using Selenium and BeautifulSoup that sends personalized connection requests to suggested profiles automatically.

Sep 5, 2020Updated May 25, 202610 min readFollow

Topics You Will Master

Automating LinkedIn login and navigation with Selenium
Scraping suggested connections with BeautifulSoup
Sending personalized connection requests programmatically
Handling pagination and dynamic page elements

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:

BASH
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:

PLAINTEXT
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.

PYTHON
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.

PYTHON
browser = webdriver.Chrome('driver/chromedriver.exe')

Open the LinkedIn login page using browser.get().

PYTHON
browser.get('https://www.linkedin.com/uas/login')

Open the config.txt file and read the username and password from it.

PYTHON
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.

PYTHON
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/.

PYTHON
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.

PYTHON
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)
OUTPUT
['/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/']
PYTHON
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.com to form fullLink, then navigates to it.
  • Finds the connect button by its class name pv-s-profile-actions and 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-message and send the personalized message using send_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 extend profilesQueued with the new IDs.
  • Introduce a random delay to mimic human browsing behavior and avoid rate limits.
PYTHON
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.txt file 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.

Found this useful? Keep building with me.

New tutorials every week on YouTube: or go deeper with a full structured course.

Find this tutorial useful?

Subscribe to our YouTube channels for more practical production walk-throughs.

Discussion & Comments