v0.6.10
[0.6.10] - 2026-07-23
Added
-
Custom DNS overrides (
STEALTH_DNS_OVERRIDES/meta["stealth"]["dns"])
Pin hostnames to fixed origin IPs sobasic/turboconnect via that address while keeping the hostname for TLS SNI, Host header, and certificate verification. Configure globally via Scrapy settings /config.STEALTH_DNS_OVERRIDES, or per-request with a bare IP ("dns": "203.0.113.10") or a{host: ip}map. Thebrowserdriver applies the effective map via a local CONNECT relay that dials the pinned IP (not Chrome--host-resolver-rules). Invalid IPs raiseValueErrorat startup / resolve time. -
Automatic PyPI update check
WhenStealthDownloaderMiddlewareis loaded, scrapy-stealth checks PyPI once per process in a background thread. If a newer version is published, an info message is printed withpip install -U scrapy-stealthand a link to that release on PyPI (e.g.https://pypi.org/project/scrapy-stealth/0.7.0/). Network errors are silent and never interrupt crawling. -
Local CI helper (
scripts/check.py,CHECK.md)
Run the same ruff, format, mypy, and pytest checks as GitHub Actions locally before pushing. -
is_browser_session_ban()— stricter ban detection for browser restarts
New helper inutils/antibot.py. HTTP block codes (403, 429, 503) always count; keyword and JS-challenge heuristics apply only to short pages (< 2500 bytes). Large HTTP 200 documents that embed anti-bot scripts (e.g. DataDome on RS Online) are no longer treated as bans.
Changed
-
**Dependency: Required for the
DnsOptionsAPI used by custom DNS overrides (ResolverOptions). -
Basic engine DNS — apply
dns_optionsonClient(...)
wreq ignores per-requestdns_options=onget()/post(); clients are now cached per(http2, dns map)like turbo sessions. -
Browser engine DNS — local CONNECT relay
Replaced unreliable--host-resolver-ruleswith a DNS-aware local proxy relay (same mechanism as proxy auth injection). Pinned hosts are dialed by IP while Chrome keeps the original hostname for TLS. -
Browser relay — silence shutdown races / dial IPs without getaddrinfo
Pinned-IP connects usesock_connectso Windows Proactor no longer hitsRuntimeError: cannot schedule new futures after shutdownwhen Chrome still CONNECT-retries during browser restart. Loop/executor teardown errors in the relay callback are swallowed. -
Browser engine — blank tab / wait / relay consistency
Disabledenable_begin_frame_controlon tab create. Browser always uses the local CONNECT relay (even with no DNS/proxy). Replaced nodriverpage.wait()with_wait_for_document._wait_for_statusreturns ~0.75s after document complete when Navigation Timing never fills._smart_waitlong-poll only for short challenge/script-only shells. Challenge HTML heuristics no longer match bareakamai/captcha/please wait./enable javascript/datadome/kasada. -
Browser engine — close tab as soon as
_smart_waitpasses
_smart_waitreturns immediately when body content is ready (settleis a max wait for growth, not a sleep after ready). HTML is captured, the fetch tab is closed via CDP, then the response is returned. Chrome is stopped when idle so the window is not left onabout:blank. -
Browser splash — show
docs/static/logo.png
_splash_url()loads the package logo viafile://when the file exists (falls back toabout:blank). -
.gitignore— ignorestealth_snapshots/, the default output directory for browser snapshots saved via the@snapshotdecorator. -
BROWSER_RESTART_COOLDOWN_S— default reduced from60to15seconds and reworked. Cooldown now spaces ban-triggered restarts when every concurrent request keeps returning 403, without blocking the first restart or all subsequent restarts for a full minute. Configurable viaconfig.BROWSER_RESTART_COOLDOWN_S. -
BanStreakTracker— restart is signalled once per ban wave (_restart_dueflag); bans during an active restart (_restarting=True) are ignored; streak resets when restart begins (acknowledge_restart()before_reset_browser()).
Fixed
-
Browser engine — false “5 consecutive bans” restarts on HTTP 200
_maybe_restartnow usesis_browser_session_ban()instead of genericis_blocked()+is_js_challenge(), which matched anti-bot script fragments in otherwise valid product pages. -
Browser engine — only one restart after 5 bans, then never again
Removed the broken 60s cooldown that treated_last_restart = 0as “just restarted” and blocked the first restart; later removed the all-or-nothing cooldown that prevented any second restart within 60s. -
Browser engine — restart storm every 1–2 seconds under concurrent 403s
Fixed duplicate restart signals when streak exceeded the threshold, bans piling up during Chrome reset, and concurrent threads all triggering_reset_browser()in the same ban wave. -
Browser engine —
CancelledErrorand empty “request failed” logs during restart
Fetches cancelled mid-restart now wait for Chrome to come back (_wait_for_browser_ready()) and retry up to 5 times. Fixed a deadlock from calling_wait_for_browser_ready()while already holding the engine lock. -
Browser engine — intermittent empty HTML on JS-heavy pages (HTTP 200)
_smart_waitno longer skips the settle delay when the body text is already long.