Anti-bot Patterns#
Most blocks aren’t Cloudflare-grade. Learn the short ladder of defences sites use β and the honest response to each rung.
β± ~8 min read Β· ~12 min hands-on π needs: Legal & Ethical Scraping Β· Hidden JSON APIs
Before you reach for stealth browsers, know the ladder. Nine times out of ten a “block” is something simple β a missing header, too many requests, or a session cookie you didn’t carry. Each rung has a correct response and an arms-race response; prefer the correct one.
βοΈ Escalating against a site that clearly doesn’t want you is a legal and ethical decision, not just a technical one. “I got past it” is not “I was allowed.” Re-read Legal & Ethical Scraping before you climb.
Try it in 5 minutes β see what you’re broadcasting#
The fastest way to understand blocking is to see what your client sends. httpbin.org echoes it back:
# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx>=0.28"]
# ///
"""See the headers your client broadcasts β and the dead giveaway.
Run: uv run whoami.py
"""
import httpx
r = httpx.get("https://httpbin.org/headers")
print(r.json()["headers"])β
Look at User-Agent: it says python-httpx/β¦. That one string is the simplest bot tell there is β and the simplest to fix honestly.
The defence ladder#
| Rung | How it spots you | The honest response |
|---|---|---|
| Rate limiting / IP block | Too many requests from one IP | Slow down, cache, respect Retry-After β Rate Limits |
| Header / User-Agent filter | Missing or python-* UA, no Accept | Send complete, honest headers (identify your bot) |
| Session / token check | No cookie or CSRF token | Reuse an httpx.Client; grab the token first β Authenticated Scraping |
| Browser fingerprinting | navigator.webdriver, headless markers | Drive a real browser β Playwright; stealth builds if permitted |
| TLS / HTTP2 fingerprint | Python’s TLS stack β a browser’s | curl_cffi impersonation β Cloudflare Bot Protection |
| CAPTCHA / Turnstile | An interactive challenge | Solve it in a browser you run, get permission, or stop |
The rungs are ordered by effort and by how far into an arms race they take you. Climb only as far as your permission does.
Set honest headers first#
Most “hard” sites relent the moment you look like a real client and behave politely:
# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx>=0.28"]
# ///
"""A polite, honestly-identified client β the baseline before any 'evasion'.
Run: uv run polite.py https://httpbin.org/get
"""
import sys
import httpx
HEADERS = {
# Identify yourself honestly, with a contact β many operators allow-list good bots.
"User-Agent": "tds-course-bot/1.0 (+mailto:[email protected])",
"Accept": "text/html,application/json;q=0.9,*/*;q=0.8",
"Accept-Language": "en",
}
with httpx.Client(headers=HEADERS, timeout=10, follow_redirects=True) as client:
r = client.get(sys.argv[1] if len(sys.argv) > 1 else "https://httpbin.org/get")
print(r.status_code, r.request.headers["User-Agent"])The arms-race rungs β and why they’re a last resort#
Proxy-rotation services and CAPTCHA-solving services (2Captcha, commercial residential-proxy pools, and the like) exist and work. But reaching for them means you’re now fighting a site that has said “no” in code β which is exactly the fact pattern that made hiQ lose on breach of contract. The stronger the wall, the louder the site is telling you to use the front door:
- Is there an API, feed, or dataset? (Sitemaps & feeds)
- Is it in an archive? (Wayback & Common Crawl)
- Can you just ask for access?
If all three are no and the Terms forbid it, the correct engineering answer is often don’t.
Your turn (β12 min)#
- Run
whoami.py, thenpolite.pyβ confirm theUser-Agentchanged. - Fetch
https://httpbin.org/status/403andhttps://httpbin.org/status/429; write down which rung each status hints at. - Pick one real site and read its
robots.txt+ Terms. Decide, in one sentence, how far up the ladder your permission actually reaches.
Checklist#
- I can list the anti-bot ladder from rate limits up to CAPTCHA.
- I send honest, complete headers before assuming a site is “hard”.
- I know which rung
curl_cffisolves and which needs a real browser. - I recognise that proxy-rotation + CAPTCHA-solving is an ethical/legal decision, not just a technical one.
- I check for an API, feed, or archive before climbing.
Go deeper#
- Cloudflare Bot Protection β the specialised, fingerprint-level version of this ladder.
- What is
robots.txtβ RFC 9309 β the rule most blocks are quietly enforcing. - Patchright β a stealth Playwright build, for the fingerprinting rung.