From 46823ff3f5959ff3d74263b9d4c75803171ad1a5 Mon Sep 17 00:00:00 2001 From: Ayush Srivastava Date: Sat, 2 Aug 2025 19:43:41 +0530 Subject: [PATCH] Add LinkedIn automation script to withdraw sent invitations --- linkedin_withdrew_request/README.md | 41 ++++++++++ .../delete_linkedin_request.py | 74 +++++++++++++++++++ linkedin_withdrew_request/requirements.txt | 1 + 3 files changed, 116 insertions(+) create mode 100644 linkedin_withdrew_request/README.md create mode 100644 linkedin_withdrew_request/delete_linkedin_request.py create mode 100644 linkedin_withdrew_request/requirements.txt diff --git a/linkedin_withdrew_request/README.md b/linkedin_withdrew_request/README.md new file mode 100644 index 000000000..6e95d373b --- /dev/null +++ b/linkedin_withdrew_request/README.md @@ -0,0 +1,41 @@ +# πŸ’Ό LinkedIn Invitation Cleanup Script + +A robust, Selenium-based automation script to withdraw all pending LinkedIn invitations. Designed with explicit waits, modular structure, and comprehensive error handling to ensure stability and reliability across Chrome and Edge browsers. + +--- + +## βš™οΈ Features + +- βœ… Automates withdrawal of sent LinkedIn invitations +- βœ… Explicit wait handling to avoid flakiness +- βœ… Cross-browser compatibility (Chrome & Edge) +- βœ… Modular functions for easy maintenance +- βœ… Graceful error handling with clear logs + +--- + +## 🧰 Requirements + +- Python 3.7+ +- Google Chrome or Microsoft Edge +- ChromeDriver / EdgeDriver installed and added to PATH + +### Python Dependencies +Install via pip: + +```bash +pip install -r requirements.txt + +πŸ›‘οΈ Best Practices +- πŸ’‘ Use explicit waits WebDriverWait instead of hard sleeps for robustness. +- 🚫 Avoid hardcoded XPaths; use stable selectors when possible. +- πŸ§ͺ Test regularly as LinkedIn’s DOM may change. +- πŸ”„ Modularize helpers to reuse and scale your automation. + +🀝 Contributing +PRs are welcome! Please follow standard formatting conventions and include docstrings. For major changes, open an issue first to discuss what you’d like to change. + +πŸ“„ License +This project is licensed under the MIT License. See LICENSE for details. + +--- diff --git a/linkedin_withdrew_request/delete_linkedin_request.py b/linkedin_withdrew_request/delete_linkedin_request.py new file mode 100644 index 000000000..aa5569267 --- /dev/null +++ b/linkedin_withdrew_request/delete_linkedin_request.py @@ -0,0 +1,74 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import ( + StaleElementReferenceException, + NoSuchElementException, + TimeoutException, +) +import time + + +def withdraw_invitations(): + driver = webdriver.Chrome() + wait = WebDriverWait(driver, 15) + driver.maximize_window() + + try: + # Step 1: Manual Login + driver.get("https://www.linkedin.com/login") + print("⏳ Please log in manually within 20 seconds...") + time.sleep(20) + + # Step 2: Navigate to Sent Invitations + driver.get("https://www.linkedin.com/mynetwork/invitation-manager/sent/") + wait.until(EC.presence_of_element_located((By.TAG_NAME, "body"))) + time.sleep(2) + + # Step 3: Loop for Withdrawals + while True: + try: + wait.until(EC.presence_of_all_elements_located( + (By.XPATH, "//span[contains(text(), 'Withdraw')]"))) + withdraw_buttons = driver.find_elements(By.XPATH, "//span[contains(text(), 'Withdraw')]") + + if not withdraw_buttons: + print("βœ… All invitations withdrawn or none left.") + break + + for button in withdraw_buttons: + try: + driver.execute_script("arguments[0].click();", button) + confirm_button = wait.until(EC.presence_of_element_located( + (By.XPATH, + "//h2[contains(text(), 'Withdraw invitation')]/ancestor::header/../div//button[contains(@aria-label,'Withdraw')]") + )) + driver.execute_script("arguments[0].click();", confirm_button) + time.sleep(2) + except (StaleElementReferenceException, NoSuchElementException, TimeoutException) as e: + print("⚠️ Skipped one due to:", e) + continue + + # Load more invitations + try: + load_more = wait.until(EC.element_to_be_clickable( + (By.XPATH, "//span[text()='Load more']") + )) + driver.execute_script("arguments[0].scrollIntoView();", load_more) + load_more.click() + time.sleep(3) + except TimeoutException: + print("πŸ“¦ No more invitations to load.") + break + + except TimeoutException: + print("⏰ Timeout while finding 'Withdraw' buttons.") + break + finally: + driver.quit() + print("πŸšͺ Done and browser closed.") + + +# Run it +withdraw_invitations() \ No newline at end of file diff --git a/linkedin_withdrew_request/requirements.txt b/linkedin_withdrew_request/requirements.txt new file mode 100644 index 000000000..6eea7baf6 --- /dev/null +++ b/linkedin_withdrew_request/requirements.txt @@ -0,0 +1 @@ +selenium>=4.0.0 \ No newline at end of file