A practical guide to ethical, effective, and legally responsible web scraping.
1. Use an official API if one exists — always prefer structured data sources.
2. Check for open datasets (data.gov, Kaggle, etc.) before scraping.
3. Contact the website and ask for data access.
4. Scrape responsibly as a last resort.
The token bucket algorithm allows bursts while maintaining an average rate:
rate_limiter = RateLimiter(requests_per_second=2.0)
await rate_limiter.acquire() # Blocks until a token is availableEnforce minimum delays between requests to the same domain to avoid overloading a single server:
scheduler = Scheduler(domain_delay=1.0) # 1 second between same-domain requestsMonitor response codes and adjust dynamically:
Retry-After header.| Signal | What It Checks |
|---|---|
| User-Agent | Known bot strings or missing headers |
| Request Rate | Unnaturally consistent timing |
| JavaScript | Whether the client executes JS |
| TLS Fingerprint | Bot-like TLS client hello patterns |
| Cookie Handling | Whether cookies are stored/sent |
| Mouse/Keyboard | Browser automation detection |
If a site actively blocks you despite reasonable measures, respect their wishes. Aggressive anti-bot bypass may violate the CFAA or equivalent laws.
Disclaimer: This is general information, not legal advice. Consult a lawyer for your specific jurisdiction.
robots.txt and Terms of Service.| Type | Use Case | Cost |
|---|---|---|
| Datacenter | High volume, low cost | $ |
| Residential | Harder to detect | $$$ |
| Mobile | Most trusted IPs | $$$$ |
| SOCKS5 | Protocol-agnostic tunneling | $$ |
Always validate scraped data before storage:
1. Clean whitespace — normalize line breaks, tabs, extra spaces.
2. Strip HTML — remove any residual markup in text fields.
3. Validate required fields — drop records missing critical data.
4. Deduplicate — content-hash based deduplication.
5. Type coercion — parse prices, dates, numbers into correct types.
For one-off or focused scraping tasks:
HttpClient → Parser → Pipeline → Storage
For broad crawling across domains:
Scheduler → [Workers] → Middleware → Parser → Pipeline → Storage
↑ │
└─────────── discovered URLs ─────────────────┘
Use HTTP for most pages, fall back to Playwright for JS-rendered content:
try:
html = await http_client.get(url)
except NeedsJavaScript:
html = await browser.get_page_content(url)Get the full Web Scraping Framework and unlock everything.
Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.
Access all interactive tools with complete data, all workload profiles, and the full scenario library.
Downloadable source code, configuration files, and working examples from every chapter.
Free updates for life. Every new chapter, tool, and improvement included.