scrapy-stealth 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. scrapy_stealth-0.1.0/LICENSE +21 -0
  2. scrapy_stealth-0.1.0/PKG-INFO +363 -0
  3. scrapy_stealth-0.1.0/README.md +307 -0
  4. scrapy_stealth-0.1.0/pyproject.toml +67 -0
  5. scrapy_stealth-0.1.0/scrapy_stealth/__init__.py +70 -0
  6. scrapy_stealth-0.1.0/scrapy_stealth/config.py +13 -0
  7. scrapy_stealth-0.1.0/scrapy_stealth/constants.py +27 -0
  8. scrapy_stealth-0.1.0/scrapy_stealth/detectors/__init__.py +3 -0
  9. scrapy_stealth-0.1.0/scrapy_stealth/detectors/antibot.py +11 -0
  10. scrapy_stealth-0.1.0/scrapy_stealth/engines/__init__.py +5 -0
  11. scrapy_stealth-0.1.0/scrapy_stealth/engines/base.py +15 -0
  12. scrapy_stealth-0.1.0/scrapy_stealth/engines/browser.py +71 -0
  13. scrapy_stealth-0.1.0/scrapy_stealth/engines/scrapy.py +8 -0
  14. scrapy_stealth-0.1.0/scrapy_stealth/exceptions.py +6 -0
  15. scrapy_stealth-0.1.0/scrapy_stealth/manager.py +18 -0
  16. scrapy_stealth-0.1.0/scrapy_stealth/middlewares/__init__.py +3 -0
  17. scrapy_stealth-0.1.0/scrapy_stealth/middlewares/stealth.py +27 -0
  18. scrapy_stealth-0.1.0/scrapy_stealth/strategies/__init__.py +5 -0
  19. scrapy_stealth-0.1.0/scrapy_stealth/strategies/fingerprint.py +15 -0
  20. scrapy_stealth-0.1.0/scrapy_stealth/strategies/proxy.py +13 -0
  21. scrapy_stealth-0.1.0/scrapy_stealth/strategies/retry.py +17 -0
  22. scrapy_stealth-0.1.0/scrapy_stealth/utils/__init__.py +0 -0
  23. scrapy_stealth-0.1.0/scrapy_stealth/utils/antibot.py +10 -0
  24. scrapy_stealth-0.1.0/scrapy_stealth/utils/browsers.py +85 -0
  25. scrapy_stealth-0.1.0/scrapy_stealth/utils/fingerprints.py +33 -0
  26. scrapy_stealth-0.1.0/scrapy_stealth/utils/headers.py +111 -0
  27. scrapy_stealth-0.1.0/scrapy_stealth/utils/proxy.py +9 -0
  28. scrapy_stealth-0.1.0/scrapy_stealth/utils/retry.py +16 -0
  29. scrapy_stealth-0.1.0/scrapy_stealth.egg-info/PKG-INFO +363 -0
  30. scrapy_stealth-0.1.0/scrapy_stealth.egg-info/SOURCES.txt +39 -0
  31. scrapy_stealth-0.1.0/scrapy_stealth.egg-info/dependency_links.txt +1 -0
  32. scrapy_stealth-0.1.0/scrapy_stealth.egg-info/requires.txt +1 -0
  33. scrapy_stealth-0.1.0/scrapy_stealth.egg-info/top_level.txt +2 -0
  34. scrapy_stealth-0.1.0/setup.cfg +4 -0
  35. scrapy_stealth-0.1.0/tests/test_config.py +13 -0
  36. scrapy_stealth-0.1.0/tests/test_detectors.py +47 -0
  37. scrapy_stealth-0.1.0/tests/test_engines.py +169 -0
  38. scrapy_stealth-0.1.0/tests/test_exceptions.py +25 -0
  39. scrapy_stealth-0.1.0/tests/test_manager.py +33 -0
  40. scrapy_stealth-0.1.0/tests/test_middleware.py +79 -0
  41. scrapy_stealth-0.1.0/tests/test_strategies.py +135 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fawad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,363 @@
1
+ Metadata-Version: 2.4
2
+ Name: scrapy-stealth
3
+ Version: 0.1.0
4
+ Summary: A pluggable stealth and anti-bot framework for Scrapy
5
+ Author-email: Fawad <fawadstar6@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Fawad
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/fawadss1/scrapy-stealth
29
+ Project-URL: Repository, https://github.com/fawadss1/scrapy-stealth
30
+ Project-URL: Issues, https://github.com/fawadss1/scrapy-stealth/issues
31
+ Project-URL: Changelog, https://github.com/fawadss1/scrapy-stealth/releases
32
+ Keywords: scrapy,web scraping,stealth,anti-bot,crawler,browser impersonation,proxy rotation,fingerprint,cloudflare bypass
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Framework :: Scrapy
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: Intended Audience :: Science/Research
37
+ Classifier: Topic :: Internet :: WWW/HTTP
38
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
39
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
40
+ Classifier: Topic :: Security
41
+ Classifier: Natural Language :: English
42
+ Classifier: Programming Language :: Python :: 3
43
+ Classifier: Programming Language :: Python :: 3.10
44
+ Classifier: Programming Language :: Python :: 3.11
45
+ Classifier: Programming Language :: Python :: 3.12
46
+ Classifier: Programming Language :: Python :: 3.13
47
+ Classifier: Programming Language :: Python :: 3 :: Only
48
+ Classifier: License :: OSI Approved :: MIT License
49
+ Classifier: Operating System :: OS Independent
50
+ Classifier: Typing :: Typed
51
+ Requires-Python: >=3.10
52
+ Description-Content-Type: text/markdown
53
+ License-File: LICENSE
54
+ Requires-Dist: rnet>=2.4.2
55
+ Dynamic: license-file
56
+
57
+ # scrapy-stealth
58
+
59
+ A pluggable anti-bot and stealth framework for Scrapy.
60
+
61
+ [![Changelog](https://img.shields.io/badge/changelog-releases-blue)](https://github.com/fawadss1/scrapy-stealth/releases)
62
+
63
+ `scrapy-stealth` extends Scrapy with browser impersonation, proxy rotation, fingerprint cycling, and intelligent retry strategies — built for large-scale, production-grade crawling.
64
+
65
+ ---
66
+
67
+ ## Features
68
+
69
+ - Pluggable engine system (`scrapy`, `stealth`, or custom)
70
+ - Browser impersonation (Chrome, Firefox, Safari, Edge, Opera — latest versions)
71
+ - Per-request engine selection via `request.meta`
72
+ - Proxy support and rotation
73
+ - Browser fingerprint rotation
74
+ - Smart retry logic that auto-escalates to stealth engine on block
75
+ - Anti-bot detection (403/429 status codes + content keyword matching)
76
+ - Thread-safe async integration
77
+
78
+ ---
79
+
80
+ ## Installation
81
+
82
+ ```bash
83
+ pip install scrapy-stealth
84
+ ```
85
+
86
+ > Requires Python 3.10+ and Scrapy 2.15+
87
+
88
+ ---
89
+
90
+ ## Quick Start
91
+
92
+ ### 1. Enable the middleware in `settings.py`
93
+
94
+ ```python
95
+ DOWNLOADER_MIDDLEWARES = {
96
+ "scrapy_stealth.middlewares.stealth.StealthDownloaderMiddleware": 950,
97
+ }
98
+ ```
99
+
100
+ ### 2. Use it in your spider
101
+
102
+ By default, requests go through the standard Scrapy engine. To use the stealth engine, set `engine` in `request.meta`:
103
+
104
+ ```python
105
+ import scrapy
106
+
107
+ class MySpider(scrapy.Spider):
108
+ name = "example"
109
+ start_urls = ["https://example.com"]
110
+
111
+ def start_requests(self):
112
+ for url in self.start_urls:
113
+ yield scrapy.Request(
114
+ url,
115
+ meta={"engine": "stealth"},
116
+ )
117
+
118
+ def parse(self, response):
119
+ self.logger.info(f"Status: {response.status}")
120
+ yield {"title": response.css("title::text").get()}
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Per-Request Configuration
126
+
127
+ All stealth options are set via `request.meta`:
128
+
129
+ | Key | Type | Description |
130
+ |---|---|---|
131
+ | `engine` | `str` | Engine to use: `"scrapy"` (default) or `"stealth"` |
132
+ | `impersonate` | `str` | Browser to impersonate: `"chrome_137"`, `"firefox_139"`, `"safari_18_5"`, etc. |
133
+ | `proxy` | `str` | Proxy URL, e.g. `"http://user:pass@host:port"` |
134
+
135
+ ### Example with all options
136
+
137
+ ```python
138
+ yield scrapy.Request(
139
+ url="https://example.com",
140
+ meta={
141
+ "engine": "stealth",
142
+ "impersonate": "firefox_139",
143
+ "proxy": "http://user:pass@proxy-host:8080",
144
+ },
145
+ )
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Strategies
151
+
152
+ ### Proxy Rotation
153
+
154
+ Use `ProxyRotator` to randomly rotate proxies across requests:
155
+
156
+ ```python
157
+ from scrapy_stealth.strategies.proxy import ProxyRotator
158
+
159
+ proxy_strategy = ProxyRotator(proxies=[
160
+ "http://proxy1:8080",
161
+ "http://proxy2:8080",
162
+ "http://proxy3:8080",
163
+ ])
164
+
165
+ yield scrapy.Request(
166
+ url="https://example.com",
167
+ meta={
168
+ "engine": "stealth",
169
+ "proxy": proxy_strategy.get(),
170
+ },
171
+ )
172
+ ```
173
+
174
+ ### Fingerprint Rotation
175
+
176
+ Use `ProfileRotator` to randomly rotate the browser fingerprint:
177
+
178
+ ```python
179
+ from scrapy_stealth.strategies.fingerprint import ProfileRotator
180
+
181
+ fp = ProfileRotator()
182
+
183
+ yield scrapy.Request(
184
+ url="https://example.com",
185
+ meta={
186
+ "engine": "stealth",
187
+ "impersonate": fp.get(), # randomly picks from latest Chrome, Firefox, Safari, Edge, Opera
188
+ },
189
+ )
190
+ ```
191
+
192
+ ### Intelligent Retry
193
+
194
+ Use `RetryHandler` in your spider or middleware to retry blocked responses with automatic engine escalation:
195
+
196
+ ```python
197
+ from scrapy_stealth.strategies.retry import RetryHandler
198
+
199
+ retry = RetryHandler()
200
+
201
+ def parse(self, response):
202
+ if retry.should_retry(response): # triggers on 403, 429, 503
203
+ yield retry.build(response.request) # retries via stealth engine
204
+ return
205
+ # normal parsing ...
206
+ ```
207
+
208
+ `build` automatically:
209
+ - Increments `retry_times` in meta
210
+ - Switches `engine` to `"stealth"`
211
+ - Sets `dont_filter=True` to bypass Scrapy's duplicate filter
212
+
213
+ ---
214
+
215
+ ## Anti-Bot Detection
216
+
217
+ Use `AntiBotDetector` to classify responses as blocked:
218
+
219
+ ```python
220
+ from scrapy_stealth.detectors.antibot import AntiBotDetector
221
+
222
+ detector = AntiBotDetector()
223
+
224
+ def parse(self, response):
225
+ if detector.is_blocked(response):
226
+ self.logger.warning("Blocked! Retrying...")
227
+ # handle retry ...
228
+ return
229
+ # normal parsing ...
230
+ ```
231
+
232
+ Detects blocks via:
233
+ - HTTP status codes: `403`, `429`
234
+ - Body keywords: `"captcha"`, `"access denied"`, `"verify you are human"`
235
+
236
+ ---
237
+
238
+ ## Full Example Spider
239
+
240
+ ```python
241
+ import scrapy
242
+ from scrapy_stealth.strategies.proxy import ProxyRotator
243
+ from scrapy_stealth.strategies.fingerprint import ProfileRotator
244
+ from scrapy_stealth.strategies.retry import RetryHandler
245
+ from scrapy_stealth.detectors.antibot import AntiBotDetector
246
+
247
+ proxy_rotator = ProxyRotator(proxies=[
248
+ "http://proxy1:8080",
249
+ "http://proxy2:8080",
250
+ ])
251
+ fp_rotator = ProfileRotator()
252
+ retry_handler = RetryHandler()
253
+ detector = AntiBotDetector()
254
+
255
+
256
+ class StealthSpider(scrapy.Spider):
257
+ name = "stealth_example"
258
+ start_urls = ["https://example.com"]
259
+
260
+ def start_requests(self):
261
+ for url in self.start_urls:
262
+ yield scrapy.Request(
263
+ url,
264
+ meta={
265
+ "engine": "stealth",
266
+ "impersonate": fp_rotator.get(),
267
+ "proxy": proxy_rotator.get(),
268
+ },
269
+ )
270
+
271
+ def parse(self, response):
272
+ if detector.is_blocked(response):
273
+ self.logger.warning("Blocked response detected, retrying...")
274
+ yield retry_handler.build(response.request)
275
+ return
276
+
277
+ yield {"title": response.css("title::text").get(), "url": response.url}
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Supported Browsers for Impersonation
283
+
284
+ | Value | Browser |
285
+ |---|---|
286
+ | `chrome_137` | Chrome 137 (default) |
287
+ | `chrome_136` | Chrome 136 |
288
+ | `chrome_135` | Chrome 135 |
289
+ | `chrome_134` | Chrome 134 |
290
+ | `chrome_133` | Chrome 133 |
291
+ | `chrome_132` | Chrome 132 |
292
+ | `chrome_131` | Chrome 131 |
293
+ | `chrome_130` | Chrome 130 |
294
+ | `chrome_129` | Chrome 129 |
295
+ | `firefox_139` | Firefox 139 |
296
+ | `firefox_136` | Firefox 136 |
297
+ | `firefox_135` | Firefox 135 |
298
+ | `firefox_133` | Firefox 133 |
299
+ | `firefox_private_136` | Firefox 136 Private/Incognito |
300
+ | `firefox_private_135` | Firefox 135 Private/Incognito |
301
+ | `firefox_android_135` | Firefox Android 135 |
302
+ | `safari_18_5` | Safari 18.5 |
303
+ | `safari_18_3_1` | Safari 18.3.1 |
304
+ | `safari_18_3` | Safari 18.3 |
305
+ | `safari_18_2` | Safari 18.2 |
306
+ | `safari_18` | Safari 18 |
307
+ | `safari_ios_18_1_1` | Safari iOS 18.1.1 |
308
+ | `safari_ios_17_4_1` | Safari iOS 17.4.1 |
309
+ | `safari_ios_17_2` | Safari iOS 17.2 |
310
+ | `safari_ipad_18` | Safari iPad 18 |
311
+ | `edge_134` | Edge 134 |
312
+ | `edge_131` | Edge 131 |
313
+ | `edge_127` | Edge 127 |
314
+ | `edge_122` | Edge 122 |
315
+ | `opera_119` | Opera 119 |
316
+ | `opera_118` | Opera 118 |
317
+ | `opera_117` | Opera 117 |
318
+ | `opera_116` | Opera 116 |
319
+ | `okhttp_5` | OkHttp 5 (Android app) |
320
+ | `okhttp_4_12` | OkHttp 4.12 (Android app) |
321
+ | `okhttp_4_10` | OkHttp 4.10 (Android app) |
322
+ | `okhttp_4_9` | OkHttp 4.9 (Android app) |
323
+ | `okhttp_3_14` | OkHttp 3.14 (Android app) |
324
+ | `okhttp_3_13` | OkHttp 3.13 (Android app) |
325
+ | `okhttp_3_11` | OkHttp 3.11 (Android app) |
326
+ | `okhttp_3_9` | OkHttp 3.9 (Android app) |
327
+
328
+ ---
329
+
330
+ ## Requirements
331
+
332
+ - Python 3.10+
333
+ - `scrapy >= 2.15.0`
334
+
335
+ ---
336
+
337
+ ## Contributing
338
+
339
+ Contributions are welcome! This is an open source project and all help is appreciated.
340
+
341
+ 1. Fork the repository on [GitHub](https://github.com/fawadss1/scrapy-stealth)
342
+ 2. Create a feature branch: `git checkout -b feature/my-feature`
343
+ 3. Make your changes and add tests if applicable
344
+ 4. Open a pull request describing what you changed and why
345
+
346
+ **Ways to contribute:**
347
+ - Report bugs via [GitHub Issues](https://github.com/fawadss1/scrapy-stealth/issues)
348
+ - Suggest new engines, strategies, or detectors
349
+ - Improve documentation or examples
350
+ - Add support for new browser fingerprints
351
+
352
+ ---
353
+
354
+ ## Changelog
355
+
356
+ See [CHANGELOG.md](CHANGELOG.md) for a full history of changes, or browse [GitHub Releases](https://github.com/fawadss1/scrapy-stealth/releases).
357
+
358
+ ---
359
+
360
+ ## License
361
+
362
+ This project is licensed under the **MIT License** — free to use, modify, and distribute.
363
+ See [LICENSE](LICENSE) for the full text.
@@ -0,0 +1,307 @@
1
+ # scrapy-stealth
2
+
3
+ A pluggable anti-bot and stealth framework for Scrapy.
4
+
5
+ [![Changelog](https://img.shields.io/badge/changelog-releases-blue)](https://github.com/fawadss1/scrapy-stealth/releases)
6
+
7
+ `scrapy-stealth` extends Scrapy with browser impersonation, proxy rotation, fingerprint cycling, and intelligent retry strategies — built for large-scale, production-grade crawling.
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ - Pluggable engine system (`scrapy`, `stealth`, or custom)
14
+ - Browser impersonation (Chrome, Firefox, Safari, Edge, Opera — latest versions)
15
+ - Per-request engine selection via `request.meta`
16
+ - Proxy support and rotation
17
+ - Browser fingerprint rotation
18
+ - Smart retry logic that auto-escalates to stealth engine on block
19
+ - Anti-bot detection (403/429 status codes + content keyword matching)
20
+ - Thread-safe async integration
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install scrapy-stealth
28
+ ```
29
+
30
+ > Requires Python 3.10+ and Scrapy 2.15+
31
+
32
+ ---
33
+
34
+ ## Quick Start
35
+
36
+ ### 1. Enable the middleware in `settings.py`
37
+
38
+ ```python
39
+ DOWNLOADER_MIDDLEWARES = {
40
+ "scrapy_stealth.middlewares.stealth.StealthDownloaderMiddleware": 950,
41
+ }
42
+ ```
43
+
44
+ ### 2. Use it in your spider
45
+
46
+ By default, requests go through the standard Scrapy engine. To use the stealth engine, set `engine` in `request.meta`:
47
+
48
+ ```python
49
+ import scrapy
50
+
51
+ class MySpider(scrapy.Spider):
52
+ name = "example"
53
+ start_urls = ["https://example.com"]
54
+
55
+ def start_requests(self):
56
+ for url in self.start_urls:
57
+ yield scrapy.Request(
58
+ url,
59
+ meta={"engine": "stealth"},
60
+ )
61
+
62
+ def parse(self, response):
63
+ self.logger.info(f"Status: {response.status}")
64
+ yield {"title": response.css("title::text").get()}
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Per-Request Configuration
70
+
71
+ All stealth options are set via `request.meta`:
72
+
73
+ | Key | Type | Description |
74
+ |---|---|---|
75
+ | `engine` | `str` | Engine to use: `"scrapy"` (default) or `"stealth"` |
76
+ | `impersonate` | `str` | Browser to impersonate: `"chrome_137"`, `"firefox_139"`, `"safari_18_5"`, etc. |
77
+ | `proxy` | `str` | Proxy URL, e.g. `"http://user:pass@host:port"` |
78
+
79
+ ### Example with all options
80
+
81
+ ```python
82
+ yield scrapy.Request(
83
+ url="https://example.com",
84
+ meta={
85
+ "engine": "stealth",
86
+ "impersonate": "firefox_139",
87
+ "proxy": "http://user:pass@proxy-host:8080",
88
+ },
89
+ )
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Strategies
95
+
96
+ ### Proxy Rotation
97
+
98
+ Use `ProxyRotator` to randomly rotate proxies across requests:
99
+
100
+ ```python
101
+ from scrapy_stealth.strategies.proxy import ProxyRotator
102
+
103
+ proxy_strategy = ProxyRotator(proxies=[
104
+ "http://proxy1:8080",
105
+ "http://proxy2:8080",
106
+ "http://proxy3:8080",
107
+ ])
108
+
109
+ yield scrapy.Request(
110
+ url="https://example.com",
111
+ meta={
112
+ "engine": "stealth",
113
+ "proxy": proxy_strategy.get(),
114
+ },
115
+ )
116
+ ```
117
+
118
+ ### Fingerprint Rotation
119
+
120
+ Use `ProfileRotator` to randomly rotate the browser fingerprint:
121
+
122
+ ```python
123
+ from scrapy_stealth.strategies.fingerprint import ProfileRotator
124
+
125
+ fp = ProfileRotator()
126
+
127
+ yield scrapy.Request(
128
+ url="https://example.com",
129
+ meta={
130
+ "engine": "stealth",
131
+ "impersonate": fp.get(), # randomly picks from latest Chrome, Firefox, Safari, Edge, Opera
132
+ },
133
+ )
134
+ ```
135
+
136
+ ### Intelligent Retry
137
+
138
+ Use `RetryHandler` in your spider or middleware to retry blocked responses with automatic engine escalation:
139
+
140
+ ```python
141
+ from scrapy_stealth.strategies.retry import RetryHandler
142
+
143
+ retry = RetryHandler()
144
+
145
+ def parse(self, response):
146
+ if retry.should_retry(response): # triggers on 403, 429, 503
147
+ yield retry.build(response.request) # retries via stealth engine
148
+ return
149
+ # normal parsing ...
150
+ ```
151
+
152
+ `build` automatically:
153
+ - Increments `retry_times` in meta
154
+ - Switches `engine` to `"stealth"`
155
+ - Sets `dont_filter=True` to bypass Scrapy's duplicate filter
156
+
157
+ ---
158
+
159
+ ## Anti-Bot Detection
160
+
161
+ Use `AntiBotDetector` to classify responses as blocked:
162
+
163
+ ```python
164
+ from scrapy_stealth.detectors.antibot import AntiBotDetector
165
+
166
+ detector = AntiBotDetector()
167
+
168
+ def parse(self, response):
169
+ if detector.is_blocked(response):
170
+ self.logger.warning("Blocked! Retrying...")
171
+ # handle retry ...
172
+ return
173
+ # normal parsing ...
174
+ ```
175
+
176
+ Detects blocks via:
177
+ - HTTP status codes: `403`, `429`
178
+ - Body keywords: `"captcha"`, `"access denied"`, `"verify you are human"`
179
+
180
+ ---
181
+
182
+ ## Full Example Spider
183
+
184
+ ```python
185
+ import scrapy
186
+ from scrapy_stealth.strategies.proxy import ProxyRotator
187
+ from scrapy_stealth.strategies.fingerprint import ProfileRotator
188
+ from scrapy_stealth.strategies.retry import RetryHandler
189
+ from scrapy_stealth.detectors.antibot import AntiBotDetector
190
+
191
+ proxy_rotator = ProxyRotator(proxies=[
192
+ "http://proxy1:8080",
193
+ "http://proxy2:8080",
194
+ ])
195
+ fp_rotator = ProfileRotator()
196
+ retry_handler = RetryHandler()
197
+ detector = AntiBotDetector()
198
+
199
+
200
+ class StealthSpider(scrapy.Spider):
201
+ name = "stealth_example"
202
+ start_urls = ["https://example.com"]
203
+
204
+ def start_requests(self):
205
+ for url in self.start_urls:
206
+ yield scrapy.Request(
207
+ url,
208
+ meta={
209
+ "engine": "stealth",
210
+ "impersonate": fp_rotator.get(),
211
+ "proxy": proxy_rotator.get(),
212
+ },
213
+ )
214
+
215
+ def parse(self, response):
216
+ if detector.is_blocked(response):
217
+ self.logger.warning("Blocked response detected, retrying...")
218
+ yield retry_handler.build(response.request)
219
+ return
220
+
221
+ yield {"title": response.css("title::text").get(), "url": response.url}
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Supported Browsers for Impersonation
227
+
228
+ | Value | Browser |
229
+ |---|---|
230
+ | `chrome_137` | Chrome 137 (default) |
231
+ | `chrome_136` | Chrome 136 |
232
+ | `chrome_135` | Chrome 135 |
233
+ | `chrome_134` | Chrome 134 |
234
+ | `chrome_133` | Chrome 133 |
235
+ | `chrome_132` | Chrome 132 |
236
+ | `chrome_131` | Chrome 131 |
237
+ | `chrome_130` | Chrome 130 |
238
+ | `chrome_129` | Chrome 129 |
239
+ | `firefox_139` | Firefox 139 |
240
+ | `firefox_136` | Firefox 136 |
241
+ | `firefox_135` | Firefox 135 |
242
+ | `firefox_133` | Firefox 133 |
243
+ | `firefox_private_136` | Firefox 136 Private/Incognito |
244
+ | `firefox_private_135` | Firefox 135 Private/Incognito |
245
+ | `firefox_android_135` | Firefox Android 135 |
246
+ | `safari_18_5` | Safari 18.5 |
247
+ | `safari_18_3_1` | Safari 18.3.1 |
248
+ | `safari_18_3` | Safari 18.3 |
249
+ | `safari_18_2` | Safari 18.2 |
250
+ | `safari_18` | Safari 18 |
251
+ | `safari_ios_18_1_1` | Safari iOS 18.1.1 |
252
+ | `safari_ios_17_4_1` | Safari iOS 17.4.1 |
253
+ | `safari_ios_17_2` | Safari iOS 17.2 |
254
+ | `safari_ipad_18` | Safari iPad 18 |
255
+ | `edge_134` | Edge 134 |
256
+ | `edge_131` | Edge 131 |
257
+ | `edge_127` | Edge 127 |
258
+ | `edge_122` | Edge 122 |
259
+ | `opera_119` | Opera 119 |
260
+ | `opera_118` | Opera 118 |
261
+ | `opera_117` | Opera 117 |
262
+ | `opera_116` | Opera 116 |
263
+ | `okhttp_5` | OkHttp 5 (Android app) |
264
+ | `okhttp_4_12` | OkHttp 4.12 (Android app) |
265
+ | `okhttp_4_10` | OkHttp 4.10 (Android app) |
266
+ | `okhttp_4_9` | OkHttp 4.9 (Android app) |
267
+ | `okhttp_3_14` | OkHttp 3.14 (Android app) |
268
+ | `okhttp_3_13` | OkHttp 3.13 (Android app) |
269
+ | `okhttp_3_11` | OkHttp 3.11 (Android app) |
270
+ | `okhttp_3_9` | OkHttp 3.9 (Android app) |
271
+
272
+ ---
273
+
274
+ ## Requirements
275
+
276
+ - Python 3.10+
277
+ - `scrapy >= 2.15.0`
278
+
279
+ ---
280
+
281
+ ## Contributing
282
+
283
+ Contributions are welcome! This is an open source project and all help is appreciated.
284
+
285
+ 1. Fork the repository on [GitHub](https://github.com/fawadss1/scrapy-stealth)
286
+ 2. Create a feature branch: `git checkout -b feature/my-feature`
287
+ 3. Make your changes and add tests if applicable
288
+ 4. Open a pull request describing what you changed and why
289
+
290
+ **Ways to contribute:**
291
+ - Report bugs via [GitHub Issues](https://github.com/fawadss1/scrapy-stealth/issues)
292
+ - Suggest new engines, strategies, or detectors
293
+ - Improve documentation or examples
294
+ - Add support for new browser fingerprints
295
+
296
+ ---
297
+
298
+ ## Changelog
299
+
300
+ See [CHANGELOG.md](CHANGELOG.md) for a full history of changes, or browse [GitHub Releases](https://github.com/fawadss1/scrapy-stealth/releases).
301
+
302
+ ---
303
+
304
+ ## License
305
+
306
+ This project is licensed under the **MIT License** — free to use, modify, and distribute.
307
+ See [LICENSE](LICENSE) for the full text.