A robust, extensible web crawler for downloading media content
Project description
Media Crawler
A robust, extensible web crawler for downloading media content from YouTube, SoundCloud, and other platforms. Built with modern Python design patterns and best practices.
๐ Table of Contents
- Features
- Installation
- Quick Start
- Usage
- Configuration
- Architecture
- Project Structure
- Development
- Testing
- Contributing
- License
โจ Features
- ๐ต Multi-Platform Support: YouTube, SoundCloud, and easily extensible to other platforms
- ๐ Smart Crawling: Depth-based crawling with configurable parameters
- โก Parallel Downloads: Multi-threaded download manager for fast processing
- ๐พ State Management: Resume interrupted crawls seamlessly
- ๐๏ธ Database Tracking: SQLite-based tracking to avoid duplicate downloads
- ๐จ Beautiful CLI: Rich progress indicators and status updates
- ๐๏ธ SOLID Design: Clean architecture with dependency injection
- ๐ก๏ธ Robust Error Handling: Automatic retries with exponential backoff
- โ๏ธ Highly Configurable: Extensive configuration options for all components
๐ฆ Installation
Prerequisites
- Python 3.8 or higher
- Chrome/Chromium browser
- ChromeDriver (or use webdriver-manager for automatic installation)
- FFmpeg (for audio conversion)
Install from source
# Clone the repository
git clone https://github.com/HasanRagab/media-crawler.git
cd media-crawler
# Create a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Install in development mode
pip install -e .
Install with pip (when published)
pip install media-crawler
๐ Quick Start
Using the Command Line
# Download from YouTube search
python cli.py youtube -k "lofi hip hop" "jazz music" -d 2
# Download from specific YouTube URLs
python cli.py youtube -u "https://youtube.com/@channel" -d 1
# Download from SoundCloud
python cli.py soundcloud -u "https://soundcloud.com/discover" -d 3
Using the Python API
from media_crawler import CrawlerFactory, ApplicationConfig
# Create configuration
config = ApplicationConfig.for_youtube()
# Create crawler
crawler = CrawlerFactory.create_crawler(
config=config,
start_urls=["https://www.youtube.com/watch?v=example"]
)
# Start crawling
crawler.crawl()
๐ Usage
Command Line Interface
The CLI provides a simple interface to the crawler functionality:
python cli.py <platform> [options]
Arguments
platform: Platform to crawl (youtubeorsoundcloud)
Options
Input Options (mutually exclusive):
-u, --urls URL [URL ...]: Starting URLs to crawl-k, --keywords KEYWORD [KEYWORD ...]: Search keywords (YouTube only)
Crawler Settings:
-d, --depth N: Maximum crawl depth (default: 2)-w, --workers N: Number of parallel download workers (default: 8)-s, --scroll N: Number of page scrolls (default: 10)
Download Settings:
-o, --output DIR: Output folder for downloads-q, --quality QUALITY: Audio quality/bitrate (default: 192)
Other Options:
-v, --verbose: Enable verbose logging--headless/--no-headless: Run browser in headless mode (default: True)--resume: Resume from previous state (default: True)--no-resume: Start fresh, clearing previous state
Examples
# Basic YouTube search with keywords
python cli.py youtube -k "ambient music" -d 2
# Multiple search terms
python cli.py youtube -k "lofi" "jazz" "chill" -d 3
# Crawl specific YouTube channels
python cli.py youtube -u "https://youtube.com/@channel1" "https://youtube.com/@channel2"
# SoundCloud with custom output directory
python cli.py soundcloud -u "https://soundcloud.com/discover" -o ~/Music/SoundCloud/
# High quality downloads with more workers
python cli.py youtube -k "classical music" -q 320 -w 16
# Verbose mode for debugging
python cli.py youtube -k "music" -d 1 -v
# Non-headless mode to see browser
python cli.py youtube -u "https://youtube.com/example" --no-headless
# Start fresh without resuming
python cli.py youtube -k "music" --no-resume
Python API
For more control, use the Python API directly:
Basic Usage
from media_crawler import (
ApplicationConfig,
CrawlerFactory,
CrawlerConfig,
DownloadConfig
)
# Create configuration
config = ApplicationConfig.for_youtube(
crawler_config=CrawlerConfig(
max_depth=2,
max_workers=8,
scroll_count=10
),
download_config=DownloadConfig(
download_folder="~/Music/YouTube/",
audio_quality="320"
)
)
# Create and run crawler
crawler = CrawlerFactory.create_crawler(
config=config,
start_urls=["https://www.youtube.com/watch?v=example"]
)
crawler.crawl()
Advanced Configuration
from media_crawler import (
ApplicationConfig,
CrawlerConfig,
DatabaseConfig,
DownloadConfig,
SeleniumConfig,
PlatformConfig
)
# Custom platform configuration
platform = PlatformConfig(
name="CustomPlatform",
base_domain="example.com",
base_url="https://example.com",
ignore_words=["login", "signup", "privacy"]
)
# Detailed configurations
crawler_config = CrawlerConfig(
max_depth=3,
max_workers=16,
scroll_count=20,
scroll_pause=0.5,
max_retries=5,
retry_backoff_base=2
)
database_config = DatabaseConfig(
db_path="data/custom.db",
check_same_thread=False
)
download_config = DownloadConfig(
download_folder="~/Downloads/Media/",
format="bestaudio/best",
audio_format="mp3",
audio_quality="320",
quiet=False
)
selenium_config = SeleniumConfig(
headless=True,
disable_gpu=True,
no_sandbox=True
)
# Create application config
config = ApplicationConfig(
platform_config=platform,
crawler_config=crawler_config,
database_config=database_config,
download_config=download_config,
selenium_config=selenium_config
)
# Create and run crawler
from media_crawler import CrawlerFactory
crawler = CrawlerFactory.create_crawler(
config=config,
start_urls=["https://example.com/start"]
)
crawler.crawl()
Error Handling
from media_crawler import (
CrawlerFactory,
ApplicationConfig,
CrawlerException,
DatabaseException,
DownloadException
)
try:
config = ApplicationConfig.for_youtube()
crawler = CrawlerFactory.create_crawler(
config=config,
start_urls=["https://www.youtube.com/watch?v=example"]
)
crawler.crawl()
except DatabaseException as e:
print(f"Database error: {e}")
except DownloadException as e:
print(f"Download error: {e}")
except CrawlerException as e:
print(f"Crawler error: {e}")
except KeyboardInterrupt:
print("Crawl interrupted by user")
โ๏ธ Configuration
Configuration Classes
The crawler uses a hierarchical configuration system:
CrawlerConfig
Controls crawler behavior:
max_depth: Maximum crawl depth (default: 2)max_workers: Number of parallel workers (default: 8)scroll_count: Number of page scrolls (default: 10)scroll_pause: Pause between scrolls in seconds (default: 0.5)max_retries: Maximum retry attempts (default: 3)retry_backoff_base: Exponential backoff base (default: 2)
DatabaseConfig
Database settings:
db_path: Path to SQLite database (default: "crawler.db")check_same_thread: SQLite thread safety (default: False)
DownloadConfig
Download settings:
download_folder: Output directory (default: "~/Music/Downloads/")format: Video/audio format (default: "bestaudio/best")audio_format: Audio codec (default: "mp3")audio_quality: Bitrate (default: "192")quiet: Suppress yt-dlp output (default: True)user_agent: HTTP user agent string
SeleniumConfig
Browser automation settings:
headless: Run browser in headless mode (default: True)disable_gpu: Disable GPU acceleration (default: True)no_sandbox: Disable Chrome sandbox (default: True)disable_dev_shm_usage: Disable /dev/shm usage (default: True)log_level: Chrome log level (default: 3)
PlatformConfig
Platform-specific settings:
name: Platform namebase_domain: Domain for URL validationbase_url: Base URL for relative linksignore_words: Words to filter out from URLs
Environment Variables
You can override default settings using environment variables:
export MEDIA_CRAWLER_DOWNLOAD_FOLDER="~/Music/Downloads/"
export MEDIA_CRAWLER_MAX_DEPTH=3
export MEDIA_CRAWLER_MAX_WORKERS=16
export MEDIA_CRAWLER_HEADLESS=true
๐๏ธ Architecture
The project follows SOLID principles and uses several design patterns:
Design Patterns
- Factory Pattern:
CrawlerFactorycreates fully configured crawler instances - Strategy Pattern: Different download and link extraction strategies
- Dependency Injection: Components are injected rather than created internally
- Interface Segregation: Abstract base classes define clear contracts
Core Components
โโโโโโโโโโโโโโโโโโโ
โ CrawlerFactory โ โ Entry point, creates all components
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโ โโโโโโโโโโโโ
โ โ Crawler โ โ Main orchestrator
โ โโโโโโฌโโโโโโ
โ โ
โ โโโโ Database (IDatabase)
โ โโโโ WebDriver (IWebDriver)
โ โโโโ DownloadManager
โ โโโโ LinkExtractor (ILinkExtractor)
โ โโโโ StateManager
โ โโโโ ProgressDisplay
โ
โโโโ ApplicationConfig
โโโ CrawlerConfig
โโโ DatabaseConfig
โโโ DownloadConfig
โโโ SeleniumConfig
โโโ PlatformConfig
Component Details
- Crawler: Main orchestration logic, manages crawl loop
- Database: Tracks downloaded content, prevents duplicates
- WebDriver: Handles page loading and JavaScript rendering
- DownloadManager: Parallel download execution with retry logic
- LinkExtractor: Platform-specific link extraction strategies
- StateManager: Saves/loads crawler state for resumption
- ProgressDisplay: Real-time progress updates
See ARCHITECTURE.md for detailed architecture documentation.
๐ Project Structure
media-crawler/
โโโ cli.py # Command-line interface
โโโ setup.py # Package setup
โโโ pyproject.toml # Project metadata
โโโ requirements.txt # Dependencies
โโโ Makefile # Build and test automation
โโโ LICENSE # MIT License
โ
โโโ media_crawler/ # Main package
โ โโโ __init__.py # Package exports
โ โโโ config.py # Configuration classes
โ โโโ crawler.py # Main crawler logic
โ โโโ factory.py # Factory for creating crawlers
โ โโโ database.py # Database interface & implementation
โ โโโ webdriver.py # Selenium WebDriver wrapper
โ โโโ downloader.py # Download manager & strategies
โ โโโ link_extractor.py # Link extraction strategies
โ โโโ state_manager.py # State persistence
โ โโโ progress.py # Progress display
โ โโโ utils.py # Utility functions
โ โโโ exceptions.py # Custom exceptions
โ
โโโ tests/ # Unit tests
โ โโโ test_config.py
โ โโโ test_crawler.py
โ โโโ test_database.py
โ โโโ ...
โ
โโโ examples/ # Example scripts
โ โโโ main.py # Basic usage example
โ โโโ examples.py # Various usage examples
โ โโโ diagnose.py # Diagnostic script
โ
โโโ docs/ # Documentation
โ โโโ ARCHITECTURE.md # Architecture details
โ โโโ API.md # API documentation
โ
โโโ scripts/ # Utility scripts
โโโ QUICKSTART.py
โโโ PROJECT_OVERVIEW.py
โโโ ORGANIZATION_SUMMARY.py
๐ง Development
Setup Development Environment
# Clone repository
git clone https://github.com/HasanRagab/media-crawler.git
cd media-crawler
# Create virtual environment
python -m venv .venv
source .venv/bin/activate
# Install development dependencies
pip install -r requirements.txt
pip install -e ".[dev]"
Code Style
The project uses:
- black for code formatting
- flake8 for linting
- mypy for type checking
# Format code
make format
# Run linters
make lint
# Type checking
make typecheck
Project Commands (Makefile)
make help # Show available commands
make install # Install package and dependencies
make dev-install # Install with development dependencies
make format # Format code with black
make lint # Run flake8 linter
make typecheck # Run mypy type checker
make test # Run tests
make test-coverage # Run tests with coverage report
make clean # Clean build artifacts
make build # Build distribution packages
๐งช Testing
Run Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=media_crawler --cov-report=html
# Run specific test file
pytest tests/test_crawler.py
# Run with verbose output
pytest -v
Write Tests
Tests are organized by module:
# tests/test_crawler.py
import pytest
from media_crawler import Crawler, ApplicationConfig
def test_crawler_initialization():
config = ApplicationConfig.for_youtube()
# ... test code
๐ค Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes
- Write tests for new functionality
- Ensure tests pass:
pytest - Format code:
make format - Commit changes:
git commit -am 'Add new feature' - Push to branch:
git push origin feature/your-feature - Submit pull request
Code Guidelines
- Follow PEP 8 style guide
- Write docstrings for all public methods
- Add type hints
- Keep functions focused and small
- Write unit tests for new features
- Update documentation
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- yt-dlp - Excellent YouTube downloader
- Selenium - Browser automation
- BeautifulSoup - HTML parsing
- Rich - Beautiful terminal formatting
๐ Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: hasanmragab@gmail.com
๐บ๏ธ Roadmap
- Add support for more platforms (Spotify, Apple Music)
- Implement GUI interface
- Add playlist management
- Support for video downloads
- Docker containerization
- Cloud storage integration
- REST API interface
- Real-time monitoring dashboard
Made with โค๏ธ by Hasan Ragab
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymedia_crawler-1.0.3.tar.gz.
File metadata
- Download URL: pymedia_crawler-1.0.3.tar.gz
- Upload date:
- Size: 60.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6060ca862a52f04fec5db6df38764ba5162414cb1d7c9b7db37c945d8b65aa64
|
|
| MD5 |
c14b70982e7ffc9b01de5f3b7f5a11b1
|
|
| BLAKE2b-256 |
9ec84332b204b6ca23036df2573cd2b15faab263cfaa850397cf7b23ee9b4e7a
|
File details
Details for the file pymedia_crawler-1.0.3-py3-none-any.whl.
File metadata
- Download URL: pymedia_crawler-1.0.3-py3-none-any.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6f7c3b9b32f12b465e567d018116cbb1489ba6898d8ba291e40a1185fc561a0
|
|
| MD5 |
f64872ce39be373994415295a03cda38
|
|
| BLAKE2b-256 |
e2fb5b66e9f33ce72fb3729740daa3e71e745c3f08cec045f0488d6a37d9b9db
|