📑 Table of Contents
How Anti-Bot Detection Works
Modern anti-bot systems use multiple layers of detection to identify automated traffic. Understanding these mechanisms is crucial for building scrapers that can access publicly available data.
IP-Based Detection
- Rate analysis — Too many requests from one IP triggers blocks
- IP reputation — Known datacenter IPs are flagged
- Geographic anomalies — Requests from unusual locations
- ASN blocking — Entire IP ranges from cloud providers blocked
Browser Fingerprinting
- Canvas and WebGL rendering patterns
- Installed fonts and plugins
- Screen resolution and color depth
- Audio context fingerprinting
- JavaScript execution timing
Behavioral Analysis
- Mouse movement patterns (bots move in straight lines)
- Scroll behavior and timing
- Keyboard typing patterns
- Time spent on page before interactions
The goal isn't to "beat" anti-bot systems, but to access publicly available data responsibly. Always respect rate limits and never overload servers.
Common Anti-Bot Systems
Cloudflare
The most widespread protection, used by millions of websites. Features include JavaScript challenges, CAPTCHA, and IP reputation scoring.
DataDome
Enterprise-grade protection using machine learning for real-time bot detection. Common on e-commerce sites.
PerimeterX (now HUMAN)
Advanced behavioral analysis and device fingerprinting. Used by major retailers and travel sites.
Ethical Scraping Principles
- Only collect publicly available data — Never bypass authentication
- Respect robots.txt — Check and follow crawl directives
- Limit request rates — Don't impact site performance for real users
- Identify yourself — Use a descriptive User-Agent when possible
- Cache responses — Don't re-fetch unchanged data
- Avoid personal data — Comply with GDPR/CCPA requirements
Proxy Rotation Strategies
Types of Proxies
- Datacenter proxies — Fast and cheap, but easily detected
- Residential proxies — Real ISP IPs, harder to detect
- Mobile proxies — 4G/5G IPs, highest trust level
- ISP proxies — Datacenter-hosted residential IPs
import random
import requests
from itertools import cycle
class ProxyRotator:
def __init__(self, proxies):
self.proxies = cycle(proxies)
self.current = next(self.proxies)
def get_proxy(self):
return {'http': self.current, 'https': self.current}
def rotate(self):
self.current = next(self.proxies)
def fetch(self, url, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.get(url, proxies=self.get_proxy(), timeout=10)
if response.status_code == 200:
return response
elif response.status_code == 429:
self.rotate() # Rate limited, switch proxy
except requests.RequestException:
self.rotate()
return None
Browser Fingerprinting
from playwright.sync_api import sync_playwright
def create_stealth_context(browser):
context = browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/120.0.0.0 Safari/537.36',
locale='en-US',
timezone_id='America/New_York',
geolocation={'latitude': 40.7128, 'longitude': -74.0060},
permissions=['geolocation'],
color_scheme='light',
device_scale_factor=1,
)
return context
# Hide automation indicators
def stealth_page(page):
page.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
""")
Rate Limiting Best Practices
import time
import random
from functools import wraps
class RateLimiter:
def __init__(self, requests_per_minute=30):
self.min_interval = 60 / requests_per_minute
self.last_request = 0
def wait(self):
elapsed = time.time() - self.last_request
if elapsed < self.min_interval:
sleep_time = self.min_interval - elapsed
sleep_time += random.uniform(0.5, 2.0) # Add jitter
time.sleep(sleep_time)
self.last_request = time.time()
# Decorator for rate limiting
def rate_limited(rpm=30):
limiter = RateLimiter(rpm)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
limiter.wait()
return func(*args, **kwargs)
return wrapper
return decorator
@rate_limited(rpm=20)
def fetch_page(url):
return requests.get(url)
Start with very conservative rate limits (10-20 requests/minute) and gradually increase while monitoring for blocks.
Handling CAPTCHAs
1. Avoid Triggering Them
The best CAPTCHA is one you never see. Follow rate limits, use quality proxies, and maintain consistent fingerprints.
2. CAPTCHA Solving Services
Services like 2Captcha or Anti-Captcha use human workers to solve challenges.
3. Session Persistence
Once you solve a CAPTCHA, save and reuse the session cookies to avoid repeated challenges.
Let Us Handle the Complexity
Crawlix manages proxy rotation, fingerprinting, and anti-bot bypass so you can focus on using your data.
Get Started →