Skip to content

Instantly share code, notes, and snippets.

@mohdabdin
Created November 29, 2023 16:36
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mohdabdin/804dc17ac1e064c28b2860cf2c37dbb1 to your computer and use it in GitHub Desktop.
Save mohdabdin/804dc17ac1e064c28b2860cf2c37dbb1 to your computer and use it in GitHub Desktop.
"""
This script is used to automate the process of sending LinkedIn connection requests and messages to a list of people.
It uses Selenium to automate the process of logging in and sending connection requests and messages.
Author: Mohammad Abdin (https://www.linkedin.com/in/mohammad-abdin-779260147/)
Keep in mind this can break if LinkedIn changes their HTML structure.
"""
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import random
import time
driver = webdriver.Chrome()
actions = ActionChains(driver)
driver.get("https://www.linkedin.com/login")
def login(un, pw):
time.sleep(1)
u = driver.find_element(By.XPATH, "/html/body/div/main/div[3]/div[1]/form/div[1]/input")
p = driver.find_element(By.XPATH, "/html/body/div/main/div[3]/div[1]/form/div[2]/input")
time.sleep(3)
u.send_keys(un)
p.send_keys(pw)
time.sleep(1)
driver.find_element(By.XPATH, "/html/body/div/main/div[3]/div[1]/form/div[3]/button").click()
time.sleep(10)
def outreach(messages_, link, n):
k = 0
page = 1
results = []
while True:
if k >= n:
break
driver.get(link + '&page=' + str(page))
page += 1
if page > 100:
driver.quit()
break
time.sleep(5)
page_results = driver.find_elements(By.CSS_SELECTOR, 'li.reusable-search__result-container')
for result in page_results:
if k >= n:
break
driver.execute_script("return arguments[0].scrollIntoView();", result)
driver.execute_script("window.scrollBy(0, -150);")
try:
name = result.find_element(By.XPATH, './/div/div/div[2]/div[1]/div[1]/div/span[1]/span/a/span/span[1]').text
headline = result.find_element(By.XPATH, './/div/div/div[2]/div[1]/div[2]/div[1]').text
location = result.find_element(By.XPATH, './/div/div/div[2]/div[1]/div[2]/div[2]').text
profile = result.find_element(By.XPATH, './/div/div/div[2]/div[1]/div[1]/div/span[1]/span/a').get_attribute('href')
cta = result.find_element(By.XPATH, './/div/div/div[3]/div/button')
cta_text = cta.find_element(By.XPATH, './/span').text
if cta_text == 'Connect':
cta.click() # Connect button
time.sleep(1)
driver.find_element(By.XPATH, '/html/body/div[3]/div/div/div[3]/button[1]').click() # add note button
time.sleep(1)
msg_body = random.choice(messages_)
first_name = name.split(' ')[0]
intro = f'Hey {first_name}! '
msg = intro + msg_body
result.find_element(By.XPATH, '/html/body/div[3]/div/div/div[2]/div/textarea').send_keys(msg) # add msg
time.sleep(1)
result.find_element(By.XPATH, '/html/body/div[3]/div/div/div[3]/button[2]').click() # send invitation
k += 1
row = [link, name, profile, headline, location, msg_body]
results.append(row)
time.sleep(0.5)
except Exception as e:
try:
driver.find_element(By.XPATH, '/html/body/div[3]/div/div/button').click()
except:
pass
df = pd.DataFrame(results, columns=['Search Link', 'Name', 'Profile Link', 'Headline', 'Location', 'Message'])
return df
### __INIT__ ###
if __name__ == '__main__':
search_link = 'https://www.linkedin.com/search/results/people/?keywords=digital%20agency%20seo&origin=SWITCH_SEARCH_VERTICAL'
# "Hey {first_name}," is added automatically
MSG = """I'm working on a no-code AI platform to help marketers incorporate recent AI leaps for internal tools and workflows.
Given your background in SEO I think you'll find it useful and I'd love to get your feedback. Would you be open to a quick chat soon? :)
"""
messages = [MSG] # add more messages to experiment, they will be chosen randomly
username = 'your_linkedin_email'
password = 'your_linkedin_password'
n_users = 50 # number of users to reach out to
login(username, password)
results = outreach(messages, search_link, n_users)
# you can save the results to a csv file or do whatever you want with them
@baberparweez
Copy link

baberparweez commented Mar 20, 2024

Updated fork with some additional functionality: https://gist.github.com/baberparweez/2baf20d4addce1dc05570c3938554a8d

  • Added dotenv for moving username and password out of code in a .env file like this:
    LINKEDIN_USERNAME=username@email.com
    LINKEDIN_PASSWORD=password

  • Added argparse for allowed keyword argument update.
    For example: python3 linkedin_outreach.py --keyword "Mixed Media Advertising Agency"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment