-
-
Save sbolel/a2b2bfde16b3ab185fbc2e2049240abc to your computer and use it in GitHub Desktop.
| /** | |
| * This script automates the process of deleting your own Instagram comments. | |
| * It deletes comments in batches to avoid hitting rate limits or breaking the page. | |
| * | |
| * WARNING: This function directly manipulates the DOM and depends on the current HTML | |
| * structure of Instagram's website to work. If Instagram implements changes to the | |
| * activity page layout, structure, or functionality, this script may break or cause | |
| * unexpected behavior. Use at your own risk and always review code before running it. | |
| * | |
| * How to use: | |
| * 1. Navigate to the Instagram comments page by going to: | |
| * https://www.instagram.com/your_activity/interactions/comments | |
| * 2. Open the developer console in your web browser: | |
| * - Chrome/Firefox: Press Ctrl+Shift+J (Windows/Linux) or Cmd+Option+J (Mac) | |
| * - Safari: Enable the Develop menu in Safari's Advanced preferences, then press Cmd+Option+C | |
| * 3. Copy and paste this entire script into the console and press Enter to run it. | |
| * | |
| * How to navigate to the comments page on instagram.com: | |
| * 1. Log in to Instagram on a desktop browser. | |
| * 2. Go to your profile by clicking on the profile icon at the bottom right. | |
| * 3. Click on "Your Activity" in the menu. | |
| * 4. Select "Interactions" and then "Comments". | |
| * 5. Follow the usage steps above to run this script. | |
| */ | |
| ;(async function () { | |
| // Constants | |
| /** @const {number} - The number of comments to delete in each batch. */ | |
| const DELETION_BATCH_SIZE = 3 | |
| /** @const {number} - The delay between actions in milliseconds. */ | |
| const DELAY_BETWEEN_ACTIONS_MS = 1000 | |
| /** @const {number} - The delay between clicking the checkboxes in milliseconds. */ | |
| const DELAY_BETWEEN_CHECKBOX_CLICKS_MS = 300 | |
| /** @const {number} - The maximum number of retries for waiting operations */ | |
| const MAX_RETRIES = 60 | |
| /** | |
| * Utility function that delays execution for a given amount of time. | |
| * @param {number} ms - The milliseconds to delay. | |
| * @returns {Promise<void>} A promise that resolves after the specified delay. | |
| */ | |
| const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) | |
| /** | |
| * Utility function that waits for an element to appear in the DOM before resolving. | |
| * @param {string} selector - The CSS selector of the element to wait for. | |
| * @param {number} [timeout=30000] - The maximum time to wait in milliseconds. | |
| * @returns {Promise<Element>} A promise that resolves with the found element. | |
| * @throws {Error} If the element is not found within the timeout period. | |
| */ | |
| const waitForElement = async (selector, timeout = 30000) => { | |
| const startTime = Date.now() | |
| while (Date.now() - startTime < timeout) { | |
| const element = document.querySelector(selector) | |
| if (element) return element | |
| await delay(100) | |
| } | |
| throw new Error(`Element with selector "${selector}" not found within ${timeout}ms`) | |
| } | |
| /** | |
| * Utility function that clicks on a given element. | |
| * @param {Element} element - The element to click. | |
| * @throws {Error} If the element is not found. | |
| */ | |
| const clickElement = async (element) => { | |
| if (!element) throw new Error('Element not found') | |
| element.click() | |
| } | |
| /** | |
| * Waits for the "Select" button to reappear after the page loads more comments | |
| * following the deletion of a batch of comments when the "Select" button | |
| * is hidden while a spinner indicates that more comments are loading. | |
| * @returns {Promise<void>} A promise that resolves when the select button reappears. | |
| * @throws {Error} If the select button is not found after maximum retries. | |
| */ | |
| const waitForSelectButton = async () => { | |
| for (let i = 0; i < MAX_RETRIES; i++) { | |
| const buttonCount = document.querySelectorAll('[role="button"]')?.length | |
| if (buttonCount > 1) return | |
| await delay(1000) | |
| } | |
| throw new Error('Select button not found after maximum retries') | |
| } | |
| /** | |
| * Deletes the currently selected comments. | |
| * @returns {Promise<void>} A promise that resolves when the comments are deleted. | |
| */ | |
| const deleteSelectedComments = async () => { | |
| try { | |
| const deleteButton = await waitForElement('[aria-label="Delete"]') | |
| await clickElement(deleteButton) | |
| await delay(DELAY_BETWEEN_ACTIONS_MS) | |
| const confirmButton = await waitForElement('button[tabindex="0"]') | |
| await clickElement(confirmButton) | |
| } catch (error) { | |
| console.error('Error during comment deletion:', error.message) | |
| } | |
| } | |
| /** | |
| * Deletes all user comments by selecting comments in batches. | |
| * @returns {Promise<void>} A promise that resolves when all comments are deleted. | |
| */ | |
| const deleteActivity = async () => { | |
| try { | |
| while (true) { | |
| const [, selectButton] = document.querySelectorAll('[role="button"]') | |
| if (!selectButton) throw new Error('Select button not found') | |
| await clickElement(selectButton) | |
| await delay(DELAY_BETWEEN_ACTIONS_MS) | |
| const checkboxes = document.querySelectorAll('[aria-label="Toggle checkbox"]') | |
| if (checkboxes.length === 0) { | |
| console.log('No more comments to delete') | |
| break | |
| } | |
| for (let i = 0; i < Math.min(DELETION_BATCH_SIZE, checkboxes.length); i++) { | |
| await clickElement(checkboxes[i]) | |
| await delay(DELAY_BETWEEN_CHECKBOX_CLICKS_MS) | |
| } | |
| await delay(DELAY_BETWEEN_ACTIONS_MS) | |
| await deleteSelectedComments() | |
| await delay(DELAY_BETWEEN_ACTIONS_MS) | |
| await waitForSelectButton() | |
| await delay(DELAY_BETWEEN_ACTIONS_MS) | |
| } | |
| } catch (error) { | |
| console.error('Error in deleteActivity:', error.message) | |
| } | |
| } | |
| // Start the deletion process | |
| try { | |
| await deleteActivity() | |
| console.log('Activity deletion completed') | |
| } catch (error) { | |
| console.error('Fatal error:', error.message) | |
| } | |
| })() |
📌 Instagram Comment Bulk Deletion Script (Python + Selenium)
Description:
Automates the deletion of your Instagram comments (or likes) in batches.
Features interactive prompts for:
- How many selections per batch
- Delay between each selection (sec)
- Delay after each batch deletion (sec)
- Customizable rate-limit retry timer
Progress and batch info shown in the terminal.
import logging
import pathlib
import platform
import random
import sys
import time
from selenium import webdriver
from selenium.common.exceptions import (
NoSuchWindowException,
TimeoutException,
StaleElementReferenceException,
)
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
logging.basicConfig(format="[%(levelname)s] Instagram Cleaner: %(message)s", level=logging.INFO)
def get_int_input(prompt, default):
while True:
try:
inp = input(f"{prompt} (default: {default}) [x]: ")
if not inp or inp.lower() == "x":
return default
value = int(inp)
if value <= 0:
print("Please enter a positive integer.")
continue
return value
except ValueError:
print("Invalid input. Please enter a number.")
def get_float_input(prompt, default):
while True:
try:
inp = input(f"{prompt} (default: {default}) [x]: ")
if not inp or inp.lower() == "x":
return default
value = float(inp)
if value <= 0:
print("Please enter a positive number.")
continue
return value
except ValueError:
print("Invalid input. Please enter a number.")
print("---- Instagram Bulk Comment Cleaner ----")
SELECTIONS_AT_ONCE = get_int_input("How many comments to select (per batch)?", 5)
DELAY_BETWEEN_SELECTS = get_float_input("Delay (seconds) between each selection?", 2.0)
DELAY_AFTER_BATCH_DELETE = get_float_input("Delay (seconds) after each batch deletion?", 10.0)
CHECK_EVERY = get_float_input("If rate limit hit, wait how many hours before retry? ", 1.0) * 3600
MODE = 1 # 1 = comments, 2 = likes
print("----------------------------------------")
LIKES_URL = "https://www.instagram.com/your_activity/interactions/likes"
COMMENTS_URL = "https://www.instagram.com/your_activity/interactions/comments"
logging.info("Starting script...")
try:
options = Options()
wd = pathlib.Path().absolute()
if platform.system() == "Windows":
options.add_argument(f"user-data-dir={wd}\\chrome-profile")
else:
options.add_argument("user-data-dir=chrome-profile")
driver = webdriver.Chrome(options=options)
logging.info("Chrome browser launched")
URL = COMMENTS_URL if MODE == 1 else LIKES_URL
driver.get(URL)
logging.info(f"Opening page: {URL}")
# Wait for user to log in manually
while True:
if driver.current_url.startswith(URL):
logging.info("Login detected")
break
try:
logging.info("Waiting for login (log in from the browser and don't click anything else)...")
wait = WebDriverWait(driver, 60)
def is_not_now_button_present(driver):
try:
div = driver.find_element(By.CSS_SELECTOR, "div[role='button']")
return div.text in ["Not now", "Plus tard"]
except:
return False
wait.until(is_not_now_button_present)
driver.find_element(By.CSS_SELECTOR, "div[role='button']").send_keys(Keys.ENTER)
logging.info("Clicked 'Not now'")
break
except TimeoutException:
pass
# Main loop
total_deleted = 0
batch_number = 1
while True:
is_clicked_select = False
while not is_clicked_select:
logging.info("Waiting for elements to load...")
time.sleep(2)
elements = driver.find_elements(By.CSS_SELECTOR, 'span[data-bloks-name="bk.components.Text"]')
for el in elements:
if el.text in ["Select", "Sélectionner"]:
if any(txt.text in ["No results", "Aucun résultat"] for txt in elements):
logging.info("No items found. Finished.")
driver.quit()
sys.exit(0)
driver.execute_script("arguments[0].click();", el)
logging.info("Clicked 'Select'")
is_clicked_select = True
break
selected_count = 0
attempts = 0
refreshed = False
while selected_count < SELECTIONS_AT_ONCE:
if refreshed:
break
time.sleep(DELAY_BETWEEN_SELECTS)
for checkbox in driver.find_elements(By.CSS_SELECTOR, '[aria-label="Toggle checkbox"]'):
try:
if not checkbox.is_selected():
driver.execute_script("arguments[0].click();", checkbox)
selected_count += 1
logging.info(f"Selected item (Total: {selected_count})")
if selected_count >= SELECTIONS_AT_ONCE:
break
except StaleElementReferenceException:
continue
attempts += 1
if attempts > 10:
logging.warning("Possible UI issue or limit. Retrying...")
time.sleep(2)
attempts = 0
# Click Delete/Unlike
deleted_this_batch = False
for span in driver.find_elements(By.CSS_SELECTOR, 'span[data-bloks-name="bk.components.TextSpan"]'):
if span.text in (["Delete", "Supprimer"] if MODE == 1 else ["Unlike", "Je n’aime plus"]):
time.sleep(1)
driver.execute_script("arguments[0].click();", span)
logging.info(f"Clicked '{span.text}' to confirm deletion (Batch #{batch_number})")
deleted_this_batch = True
break
# Confirm the deletion
confirmed = False
while not confirmed and deleted_this_batch:
time.sleep(1)
for btn in driver.find_elements(By.CSS_SELECTOR, 'div[role="dialog"] button'):
try:
confirmation_text = btn.find_element(By.CSS_SELECTOR, "div").text
if confirmation_text in ["Delete", "Unlike", "Supprimer", "Je n’aime plus"]:
driver.execute_script("arguments[0].click();", btn)
logging.info(f"Confirmed '{confirmation_text}'")
total_deleted += selected_count
logging.info(f"Deleted {selected_count} comments in batch #{batch_number}. Total deleted: {total_deleted}")
batch_number += 1
confirmed = True
break
except StaleElementReferenceException:
continue
if not confirmed:
time.sleep(1)
logging.info(f"Waiting {DELAY_AFTER_BATCH_DELETE} seconds before next batch...")
time.sleep(DELAY_AFTER_BATCH_DELETE) # Extra delay for safety
except KeyboardInterrupt:
logging.info("Script manually interrupted.")
driver.quit()
sys.exit(0)
except NoSuchWindowException:
logging.exception("Browser window was closed.")
sys.exit(1)
except Exception as e:
logging.exception("Unexpected error:")
try:
driver.quit()
except:
pass
sys.exit(1)
Just used @kimonneuhoff version successfully. Took about 2 hours, I had to do a lot of clicking "ok" through periodic "we failed to delete" errors and sometimes re-paste in the code and rerun but it worked! 7 Years worth. Amazing. Thank you all who have contributed. As one other mentioned, you can easily modify and do likes too. Burning through all those now. Edit: works with story replies too xD