Last active
October 15, 2025 11:57
-
-
Save Bugaddr/1450f3d94b612fec2ee9a8d3a6f684e7 to your computer and use it in GitHub Desktop.
Python script to subscribe automatically to youtube channels using selenium
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Example channels.txt | |
| #https://www.youtube.com/channel/UCzml9bXoEM0itbcE96CB03w DroneBot Workshop | |
| #https://www.youtube.com/channel/UCzWQYUVCpZqtN93H8RR44Qw Seeker | |
| #https://www.youtube.com/channel/UCyPYQTT20IgzVw92LDvtClw Squat University | |
| from selenium import webdriver | |
| from selenium.webdriver.firefox.options import Options | |
| from selenium.webdriver.firefox.service import Service | |
| 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 TimeoutException, NoSuchElementException | |
| import time | |
| # Try to import webdriver_manager, fall back to manual setup | |
| try: | |
| from webdriver_manager.firefox import GeckoDriverManager | |
| USE_WEBDRIVER_MANAGER = True | |
| except ImportError: | |
| USE_WEBDRIVER_MANAGER = False | |
| print("Note: webdriver_manager not found. Using system GeckoDriver.") | |
| def read_channels(filename): | |
| """Read YouTube channels from file.""" | |
| channels = [] | |
| with open(filename, 'r', encoding='utf-8') as f: | |
| for line in f: | |
| line = line.strip() | |
| if line and line.startswith('https://'): | |
| url, name = line.split('\t') | |
| channels.append({'url': url, 'name': name}) | |
| return channels | |
| def setup_driver(): | |
| """Setup Firefox driver with user profile to maintain login.""" | |
| options = Options() | |
| # Use your existing Firefox profile to stay logged in | |
| # To find your profile path: | |
| # 1. Open Firefox and type: about:profiles | |
| # 2. Copy the "Root Directory" path of your default profile | |
| # Uncomment and modify the path below: | |
| # Windows Example: | |
| # options.add_argument("-profile") | |
| # options.add_argument(r"C:\Users\YourUsername\AppData\Roaming\Mozilla\Firefox\Profiles\xxxxxxxx.default-release") | |
| # Linux Example: | |
| options.add_argument("-profile") | |
| options.add_argument("/home/bugaddr/.mozilla/firefox/wmptiknq.default-release") | |
| # Mac Example: | |
| # options.add_argument("-profile") | |
| # options.add_argument("/Users/yourusername/Library/Application Support/Firefox/Profiles/xxxxxxxx.default-release") | |
| # Setup driver | |
| if USE_WEBDRIVER_MANAGER: | |
| service = Service(GeckoDriverManager().install()) | |
| driver = webdriver.Firefox(service=service, options=options) | |
| else: | |
| # If webdriver_manager is not installed, use system geckodriver | |
| # Make sure geckodriver is in your PATH or specify the path: | |
| # service = Service('/path/to/geckodriver') # Uncomment and set path if needed | |
| driver = webdriver.Firefox(options=options) | |
| driver.maximize_window() | |
| return driver | |
| def wait_for_login(driver): | |
| """Wait for user to manually log in to YouTube.""" | |
| print("\n" + "="*60) | |
| print("PLEASE LOG IN TO YOUTUBE") | |
| print("="*60) | |
| print("The browser will open YouTube's homepage.") | |
| print("Please log in to your YouTube account.") | |
| print("After logging in, return here and press Enter to continue.") | |
| print("="*60 + "\n") | |
| driver.get("https://www.youtube.com") | |
| input("Press Enter after you've logged in to YouTube...") | |
| def subscribe_to_channel(driver, channel_url, channel_name, wait_time=10): | |
| """ | |
| Navigate to a channel and click the Subscribe button. | |
| Returns: | |
| str: Status of subscription attempt | |
| """ | |
| try: | |
| driver.get(channel_url) | |
| time.sleep(2) # Wait for page to start loading | |
| wait = WebDriverWait(driver, wait_time) | |
| # Try to find Subscribe button - YouTube uses different selectors | |
| subscribe_button = None | |
| selectors = [ | |
| "//button[contains(@aria-label, 'Subscribe')]", | |
| "//yt-button-shape//button[contains(., 'Subscribe')]", | |
| "//button[@aria-label='Subscribe to']", | |
| "//ytd-subscribe-button-renderer//button", | |
| "//button[contains(text(), 'Subscribe')]" | |
| ] | |
| for selector in selectors: | |
| try: | |
| subscribe_button = wait.until( | |
| EC.element_to_be_clickable((By.XPATH, selector)) | |
| ) | |
| break | |
| except TimeoutException: | |
| continue | |
| if subscribe_button: | |
| # Check if already subscribed | |
| button_text = subscribe_button.get_attribute('aria-label') or subscribe_button.text | |
| if 'subscribed' in button_text.lower(): | |
| return "Already Subscribed" | |
| # Click the Subscribe button | |
| subscribe_button.click() | |
| time.sleep(1) | |
| # Handle notification preferences popup if it appears | |
| try: | |
| notification_button = WebDriverWait(driver, 3).until( | |
| EC.element_to_be_clickable((By.XPATH, "//button[@aria-label='All']")) | |
| ) | |
| notification_button.click() | |
| time.sleep(0.5) | |
| except: | |
| pass # Popup might not appear | |
| return "Subscribed ✓" | |
| else: | |
| return "Subscribe button not found" | |
| except TimeoutException: | |
| return "Timeout - page took too long to load" | |
| except Exception as e: | |
| return f"Error: {str(e)[:50]}" | |
| def main(): | |
| filename = 'channels.txt' | |
| print("YouTube Auto-Subscriber with Selenium (Firefox)") | |
| print("=" * 60) | |
| try: | |
| channels = read_channels(filename) | |
| print(f"\nFound {len(channels)} channels to subscribe to.") | |
| # Setup driver | |
| print("\nSetting up Firefox browser...") | |
| driver = setup_driver() | |
| # Wait for login | |
| wait_for_login(driver) | |
| # Start subscribing | |
| print("\n" + "=" * 60) | |
| print("STARTING SUBSCRIPTION PROCESS") | |
| print("=" * 60 + "\n") | |
| success_count = 0 | |
| already_subscribed = 0 | |
| failed_count = 0 | |
| for i, channel in enumerate(channels, 1): | |
| print(f"[{i}/{len(channels)}] {channel['name']:<40}", end=" ... ") | |
| status = subscribe_to_channel(driver, channel['url'], channel['name']) | |
| print(status) | |
| if status == "Subscribed ✓": | |
| success_count += 1 | |
| elif status == "Already Subscribed": | |
| already_subscribed += 1 | |
| else: | |
| failed_count += 1 | |
| # Small delay between channels | |
| time.sleep(1.5) | |
| # Summary | |
| print("\n" + "=" * 60) | |
| print("SUBSCRIPTION COMPLETE!") | |
| print("=" * 60) | |
| print(f"✓ Successfully subscribed: {success_count}") | |
| print(f"• Already subscribed: {already_subscribed}") | |
| print(f"✗ Failed: {failed_count}") | |
| print("=" * 60) | |
| input("\nPress Enter to close the browser...") | |
| driver.quit() | |
| except FileNotFoundError: | |
| print(f"Error: Could not find '{filename}'") | |
| except Exception as e: | |
| print(f"Error: {e}") | |
| if 'driver' in locals(): | |
| driver.quit() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment