oniscrape 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. oniscrape-0.1.0/.gitignore +43 -0
  2. oniscrape-0.1.0/LICENSE +21 -0
  3. oniscrape-0.1.0/PKG-INFO +261 -0
  4. oniscrape-0.1.0/README.md +226 -0
  5. oniscrape-0.1.0/demo.py +103 -0
  6. oniscrape-0.1.0/examples/01_nextjs_and_rsc.py +37 -0
  7. oniscrape-0.1.0/examples/02_threads_meta_graphql.py +29 -0
  8. oniscrape-0.1.0/examples/03_proxy_rotation_and_cooldown.py +35 -0
  9. oniscrape-0.1.0/examples/04_fast_dom_lexbor.py +29 -0
  10. oniscrape-0.1.0/pyproject.toml +75 -0
  11. oniscrape-0.1.0/src/fast_scraper/__init__.py +37 -0
  12. oniscrape-0.1.0/src/fast_scraper/client.py +175 -0
  13. oniscrape-0.1.0/src/fast_scraper/config.py +49 -0
  14. oniscrape-0.1.0/src/fast_scraper/cookies/__init__.py +5 -0
  15. oniscrape-0.1.0/src/fast_scraper/cookies/base.py +20 -0
  16. oniscrape-0.1.0/src/fast_scraper/cookies/memory.py +28 -0
  17. oniscrape-0.1.0/src/fast_scraper/cookies/redis_storage.py +38 -0
  18. oniscrape-0.1.0/src/fast_scraper/dom/__init__.py +15 -0
  19. oniscrape-0.1.0/src/fast_scraper/dom/parser.py +52 -0
  20. oniscrape-0.1.0/src/fast_scraper/extractors/__init__.py +14 -0
  21. oniscrape-0.1.0/src/fast_scraper/extractors/base.py +6 -0
  22. oniscrape-0.1.0/src/fast_scraper/extractors/generic.py +50 -0
  23. oniscrape-0.1.0/src/fast_scraper/extractors/meta.py +53 -0
  24. oniscrape-0.1.0/src/fast_scraper/extractors/nextjs.py +100 -0
  25. oniscrape-0.1.0/src/fast_scraper/extractors/nuxt.py +29 -0
  26. oniscrape-0.1.0/src/fast_scraper/pipeline/__init__.py +3 -0
  27. oniscrape-0.1.0/src/fast_scraper/pipeline/schema_mapper.py +55 -0
  28. oniscrape-0.1.0/src/fast_scraper/proxy/__init__.py +4 -0
  29. oniscrape-0.1.0/src/fast_scraper/proxy/health.py +38 -0
  30. oniscrape-0.1.0/src/fast_scraper/proxy/manager.py +69 -0
  31. oniscrape-0.1.0/src/fast_scraper/py.typed +1 -0
  32. oniscrape-0.1.0/src/oniscrape/__init__.py +49 -0
  33. oniscrape-0.1.0/src/oniscrape/py.typed +1 -0
  34. oniscrape-0.1.0/tests/test_client.py +13 -0
  35. oniscrape-0.1.0/tests/test_dom_parser.py +34 -0
  36. oniscrape-0.1.0/tests/test_generic_extractor.py +38 -0
  37. oniscrape-0.1.0/tests/test_meta_extractor.py +40 -0
  38. oniscrape-0.1.0/tests/test_nextjs_extractor.py +71 -0
  39. oniscrape-0.1.0/tests/test_proxy_manager.py +35 -0
  40. oniscrape-0.1.0/tests/test_schema_mapper.py +66 -0
  41. oniscrape-0.1.0/uv.lock +615 -0
@@ -0,0 +1,43 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environments
24
+ .env
25
+ .venv
26
+ env/
27
+ venv/
28
+ ENV/
29
+
30
+ # Testing & Coverage
31
+ .pytest_cache/
32
+ .coverage
33
+ htmlcov/
34
+
35
+ # Linter & Typecheck
36
+ .ruff_cache/
37
+ .mypy_cache/
38
+ .pyright/
39
+
40
+ # IDE
41
+ .vscode/
42
+ .idea/
43
+ *.swp
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FastScraper Authors
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,261 @@
1
+ Metadata-Version: 2.5
2
+ Name: oniscrape
3
+ Version: 0.1.0
4
+ Summary: High-performance async scraping engine powered by curl_cffi, selectolax, orjson, and Pydantic v2. Extracts Next.js SSR, RSC Flight streams, and Meta Relay states without headless browsers.
5
+ Author: FastScraper Team
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: anti-bot,crawler,curl-cffi,ja3,nextjs,pydantic,react-server-components,rsc,scraper,selectolax,tls-fingerprint
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Internet :: WWW/HTTP
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: curl-cffi>=0.7.4
22
+ Requires-Dist: jmespath>=1.0.1
23
+ Requires-Dist: orjson>=3.10.0
24
+ Requires-Dist: pydantic>=2.7.0
25
+ Requires-Dist: selectolax>=0.3.21
26
+ Requires-Dist: tenacity>=8.3.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pyright>=1.1.350; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
30
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
31
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
32
+ Provides-Extra: redis
33
+ Requires-Dist: redis>=5.0.0; extra == 'redis'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # fast-scraper 🚀
37
+
38
+ > **Высокопроизводительный асинхронный скрапинг-движок для Python на базе `curl_cffi`, `selectolax` (Lexbor), `orjson` и `Pydantic v2`.**
39
+
40
+ Специализированный инструмент для сверхбыстрого парсинга современных веб-приложений (Next.js, React Server Components, Meta Threads/Instagram, Nuxt) и статических сайтов **без запуска тяжелых headless-браузеров**.
41
+
42
+ ---
43
+
44
+ ## ⚡ Почему не Playwright / Selenium?
45
+
46
+ | Параметр | Headless Браузер (Playwright / Puppeteer) | `fast-scraper` (No-JS / State Engine) |
47
+ | :--- | :--- | :--- |
48
+ | **Потребление RAM** | ~300 - 600 МБ на поток/вкладку | **~15 - 30 МБ на процесс** |
49
+ | **Время ответа** | 3.0 - 8.0 секунд (рендеринг DOM + JS) | **0.1 - 0.4 секунды** (чистый сетевой I/O) |
50
+ | **Устойчивость к редизайнам** | Низкая (CSS-классы React меняются при каждом билде) | **Высокая** (структура данных в State/API стабильна годами) |
51
+ | **Обход TLS-проверок** | Зависит от stealth-патчей Chromium | **Нативный JA3/JA4 / HTTP2 спуфинг через `curl_cffi`** |
52
+ | **Утечки IP при смене прокси** | Частая проблема из-за общего пула сокетов | **Гарантированная изоляция сокетов на уровне сессий** |
53
+
54
+ ---
55
+
56
+ ## 📦 Установка
57
+
58
+ ### Базовая установка:
59
+ ```bash
60
+ pip install fast-scraper
61
+ ```
62
+ или через `uv`:
63
+ ```bash
64
+ uv add fast-scraper
65
+ ```
66
+
67
+ ### С поддержкой Redis (для распределенного хранения куки):
68
+ ```bash
69
+ pip install fast-scraper[redis]
70
+ ```
71
+
72
+ ### Локальная разработка:
73
+ ```bash
74
+ git clone https://github.com/your-org/fast-scraper.git
75
+ cd fast-scraper
76
+ uv pip install -e .[dev]
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 🛠️ Быстрый старт и примеры
82
+
83
+ ### 1. Парсинг Next.js SSR (__NEXT_DATA__) напрямую в Pydantic v2
84
+
85
+ Вместо того чтобы искать элементы на странице, `fast-scraper` мгновенно забирает сырое JSON-состояние гидратации, вшитое сервером в страницу:
86
+
87
+ ```python
88
+ import asyncio
89
+ from pydantic import BaseModel, Field
90
+ from fast_scraper import ScraperClient, extract_next_data, map_state_to_model
91
+
92
+ class CryptoMetric(BaseModel):
93
+ num_cryptos: int = Field(alias="numCryptocurrencies")
94
+ num_markets: int = Field(alias="numMarkets")
95
+ active_exchanges: int = Field(alias="activeExchanges")
96
+
97
+ async def main():
98
+ async with ScraperClient() as client:
99
+ response = await client.get("https://coinmarketcap.com/")
100
+
101
+ # 1. Мгновенно достаем JSON-дерево гидратации (без выполнения JS)
102
+ state = extract_next_data(response.text)
103
+
104
+ # 2. Извлекаем нужные ветки по JMESPath и валидируем через Pydantic v2
105
+ metrics = map_state_to_model(
106
+ state=state,
107
+ query="props.dehydratedState.queries[?queryKey[0]=='global-metric'].state.data | [0]",
108
+ model_cls=CryptoMetric,
109
+ strict=False,
110
+ )
111
+ print(metrics)
112
+
113
+ asyncio.run(main())
114
+ ```
115
+
116
+ ---
117
+
118
+ ### 2. Парсинг React Server Components (RSC Flight stream) в Next.js 14 / 15 / 19
119
+
120
+ Next.js App Router не использует `__NEXT_DATA__`, а отдает потоковые чанки `self.__next_f.push` по протоколу React Flight (`id:tag:payload`). `fast-scraper` декодирует этот протокол "из коробки":
121
+
122
+ ```python
123
+ import asyncio
124
+ from fast_scraper import ScraperClient, extract_rsc_flight
125
+
126
+ async def main():
127
+ async with ScraperClient() as client:
128
+ response = await client.get("https://nextjs.org/")
129
+
130
+ # Декодирует все потоковые слоты wire-протокола
131
+ flight_tree = extract_rsc_flight(response.text)
132
+
133
+ # flight_tree содержит разобранные слоты вида {'0_J': [...], '1_I': [...]}
134
+ print(f"Декодировано слотов RSC: {len(flight_tree)}")
135
+
136
+ asyncio.run(main())
137
+ ```
138
+
139
+ ---
140
+
141
+ ### 3. Парсинг закрытых данных Threads / Instagram (Meta Relay Cache)
142
+
143
+ ```python
144
+ import asyncio
145
+ from fast_scraper import ScraperClient, extract_meta_relay, extract_meta_tokens
146
+
147
+ async def main():
148
+ async with ScraperClient() as client:
149
+ response = await client.get("https://www.threads.net/@zuck")
150
+
151
+ # 1. Извлечение токенов для последующих прямых POST-запросов к /api/graphql
152
+ tokens = extract_meta_tokens(response.text)
153
+ print("LSD Token:", tokens["lsd"])
154
+ print("App ID:", tokens["app_id"])
155
+
156
+ # 2. Извлечение префетч-кэша GraphQL (RelayPrefetchedStreamCache)
157
+ relay_data = extract_meta_relay(response.text)
158
+ print(f"Найдено блоков Relay Cache: {len(relay_data)}")
159
+
160
+ asyncio.run(main())
161
+ ```
162
+
163
+ ---
164
+
165
+ ### 4. C-уровень DOM-парсинга через Selectolax Lexbor
166
+
167
+ Для сайтов с чистым HTML используется движок Lexbor на чистом Си (в 20 раз быстрее BeautifulSoup и потребляет минимум RAM):
168
+
169
+ ```python
170
+ from fast_scraper import parse_html, css_text, css_all_text, css_all_attr
171
+
172
+ tree = parse_html(html_content)
173
+
174
+ # Быстрое извлечение первого найденного текста с авто-strip
175
+ title = css_text(tree, "h1.product-title", default="Без названия")
176
+
177
+ # Извлечение всех элементов
178
+ items = css_all_text(tree, "ul.features li")
179
+
180
+ # Извлечение атрибутов
181
+ image_urls = css_all_attr(tree, "div.gallery img", "src")
182
+ ```
183
+
184
+ ---
185
+
186
+ ### 5. Пул прокси с авто-кулдауном и предотвращением утечек сокетов
187
+
188
+ `fast-scraper` изолирует `AsyncSession` под каждый конкретный запрос/прокси, что гарантирует закрытие Keep-Alive сокетов `libcurl` и предотвращает утечки реального IP при ротации:
189
+
190
+ ```python
191
+ from fast_scraper import ScraperClient, ScraperConfig, ProxyConfig
192
+
193
+ config = ScraperConfig(
194
+ default_impersonate="chrome124",
195
+ timeout_seconds=15.0,
196
+ proxy=ProxyConfig(
197
+ urls=[
198
+ "socks5://127.0.0.1:40000", # Например, локальный Cloudflare WARP
199
+ "socks5://127.0.0.1:40001",
200
+ "http://user:pass@proxy3.net:8080",
201
+ ],
202
+ strategy="least_failed", # "round_robin" | "random" | "least_failed"
203
+ cooldown_seconds=120.0, # Время отстоя прокси при получении 403/429
204
+ max_fails_before_cooldown=2,
205
+ )
206
+ )
207
+
208
+ async with ScraperClient(config=config) as client:
209
+ response = await client.get("https://httpbin.org/ip")
210
+ print(response.json())
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 🏛️ Архитектура пакета
216
+
217
+ ```text
218
+ src/fast_scraper/
219
+ ├── __init__.py # Публичный API
220
+ ├── client.py # ScraperClient с изоляцией сессий и ретраями (tenacity)
221
+ ├── config.py # Pydantic v2 конфигурация (ScraperConfig, ProxyConfig, RetryConfig)
222
+ ├── py.typed # Маркер типизации PEP 561
223
+ ├── extractors/ # Движки извлечения состояний гидратации
224
+ │ ├── nextjs.py # __NEXT_DATA__ и React Server Components (RSC Flight)
225
+ │ ├── meta.py # Threads / Instagram Relay Cache и токены (lsd, app_id)
226
+ │ ├── nuxt.py # Nuxt 2 / 3 (__NUXT_DATA__)
227
+ │ └── generic.py # Schema.org JSON-LD и <script type="application/json">
228
+ ├── dom/ # C-уровень DOM-парсинга
229
+ │ └── parser.py # Обертки над Selectolax Lexbor
230
+ ├── proxy/ # Ротация и мониторинг прокси
231
+ │ ├── manager.py # ProxyManager (Round-Robin, Random, Least-Failed)
232
+ │ └── health.py # Отслеживание сбоев и таймеры кулдауна
233
+ ├── cookies/ # Управление сессиями и куки
234
+ │ ├── memory.py # InMemoryCookieStorage
235
+ │ └── redis_storage.py # RedisCookieStorage
236
+ └── pipeline/ # Трансформация данных
237
+ └── schema_mapper.py # JMESPath -> Pydantic v2 TypeAdapter
238
+ ```
239
+
240
+ ---
241
+
242
+ ## 🧪 Тестирование и верификация
243
+
244
+ Все тесты и проверки запускаются одной командой:
245
+
246
+ ```bash
247
+ # Запуск тестов
248
+ uv run --with pytest --with pytest-asyncio pytest -v
249
+
250
+ # Проверка типов
251
+ uv run --with pyright --with pytest pyright src tests
252
+
253
+ # Проверка линтером
254
+ uv run --with ruff ruff check src tests
255
+ ```
256
+
257
+ ---
258
+
259
+ ## 📄 Лицензия
260
+
261
+ Проект распространяется под свободной лицензией **MIT**. Подробнее см. в файле [LICENSE](LICENSE).
@@ -0,0 +1,226 @@
1
+ # fast-scraper 🚀
2
+
3
+ > **Высокопроизводительный асинхронный скрапинг-движок для Python на базе `curl_cffi`, `selectolax` (Lexbor), `orjson` и `Pydantic v2`.**
4
+
5
+ Специализированный инструмент для сверхбыстрого парсинга современных веб-приложений (Next.js, React Server Components, Meta Threads/Instagram, Nuxt) и статических сайтов **без запуска тяжелых headless-браузеров**.
6
+
7
+ ---
8
+
9
+ ## ⚡ Почему не Playwright / Selenium?
10
+
11
+ | Параметр | Headless Браузер (Playwright / Puppeteer) | `fast-scraper` (No-JS / State Engine) |
12
+ | :--- | :--- | :--- |
13
+ | **Потребление RAM** | ~300 - 600 МБ на поток/вкладку | **~15 - 30 МБ на процесс** |
14
+ | **Время ответа** | 3.0 - 8.0 секунд (рендеринг DOM + JS) | **0.1 - 0.4 секунды** (чистый сетевой I/O) |
15
+ | **Устойчивость к редизайнам** | Низкая (CSS-классы React меняются при каждом билде) | **Высокая** (структура данных в State/API стабильна годами) |
16
+ | **Обход TLS-проверок** | Зависит от stealth-патчей Chromium | **Нативный JA3/JA4 / HTTP2 спуфинг через `curl_cffi`** |
17
+ | **Утечки IP при смене прокси** | Частая проблема из-за общего пула сокетов | **Гарантированная изоляция сокетов на уровне сессий** |
18
+
19
+ ---
20
+
21
+ ## 📦 Установка
22
+
23
+ ### Базовая установка:
24
+ ```bash
25
+ pip install fast-scraper
26
+ ```
27
+ или через `uv`:
28
+ ```bash
29
+ uv add fast-scraper
30
+ ```
31
+
32
+ ### С поддержкой Redis (для распределенного хранения куки):
33
+ ```bash
34
+ pip install fast-scraper[redis]
35
+ ```
36
+
37
+ ### Локальная разработка:
38
+ ```bash
39
+ git clone https://github.com/your-org/fast-scraper.git
40
+ cd fast-scraper
41
+ uv pip install -e .[dev]
42
+ ```
43
+
44
+ ---
45
+
46
+ ## 🛠️ Быстрый старт и примеры
47
+
48
+ ### 1. Парсинг Next.js SSR (__NEXT_DATA__) напрямую в Pydantic v2
49
+
50
+ Вместо того чтобы искать элементы на странице, `fast-scraper` мгновенно забирает сырое JSON-состояние гидратации, вшитое сервером в страницу:
51
+
52
+ ```python
53
+ import asyncio
54
+ from pydantic import BaseModel, Field
55
+ from fast_scraper import ScraperClient, extract_next_data, map_state_to_model
56
+
57
+ class CryptoMetric(BaseModel):
58
+ num_cryptos: int = Field(alias="numCryptocurrencies")
59
+ num_markets: int = Field(alias="numMarkets")
60
+ active_exchanges: int = Field(alias="activeExchanges")
61
+
62
+ async def main():
63
+ async with ScraperClient() as client:
64
+ response = await client.get("https://coinmarketcap.com/")
65
+
66
+ # 1. Мгновенно достаем JSON-дерево гидратации (без выполнения JS)
67
+ state = extract_next_data(response.text)
68
+
69
+ # 2. Извлекаем нужные ветки по JMESPath и валидируем через Pydantic v2
70
+ metrics = map_state_to_model(
71
+ state=state,
72
+ query="props.dehydratedState.queries[?queryKey[0]=='global-metric'].state.data | [0]",
73
+ model_cls=CryptoMetric,
74
+ strict=False,
75
+ )
76
+ print(metrics)
77
+
78
+ asyncio.run(main())
79
+ ```
80
+
81
+ ---
82
+
83
+ ### 2. Парсинг React Server Components (RSC Flight stream) в Next.js 14 / 15 / 19
84
+
85
+ Next.js App Router не использует `__NEXT_DATA__`, а отдает потоковые чанки `self.__next_f.push` по протоколу React Flight (`id:tag:payload`). `fast-scraper` декодирует этот протокол "из коробки":
86
+
87
+ ```python
88
+ import asyncio
89
+ from fast_scraper import ScraperClient, extract_rsc_flight
90
+
91
+ async def main():
92
+ async with ScraperClient() as client:
93
+ response = await client.get("https://nextjs.org/")
94
+
95
+ # Декодирует все потоковые слоты wire-протокола
96
+ flight_tree = extract_rsc_flight(response.text)
97
+
98
+ # flight_tree содержит разобранные слоты вида {'0_J': [...], '1_I': [...]}
99
+ print(f"Декодировано слотов RSC: {len(flight_tree)}")
100
+
101
+ asyncio.run(main())
102
+ ```
103
+
104
+ ---
105
+
106
+ ### 3. Парсинг закрытых данных Threads / Instagram (Meta Relay Cache)
107
+
108
+ ```python
109
+ import asyncio
110
+ from fast_scraper import ScraperClient, extract_meta_relay, extract_meta_tokens
111
+
112
+ async def main():
113
+ async with ScraperClient() as client:
114
+ response = await client.get("https://www.threads.net/@zuck")
115
+
116
+ # 1. Извлечение токенов для последующих прямых POST-запросов к /api/graphql
117
+ tokens = extract_meta_tokens(response.text)
118
+ print("LSD Token:", tokens["lsd"])
119
+ print("App ID:", tokens["app_id"])
120
+
121
+ # 2. Извлечение префетч-кэша GraphQL (RelayPrefetchedStreamCache)
122
+ relay_data = extract_meta_relay(response.text)
123
+ print(f"Найдено блоков Relay Cache: {len(relay_data)}")
124
+
125
+ asyncio.run(main())
126
+ ```
127
+
128
+ ---
129
+
130
+ ### 4. C-уровень DOM-парсинга через Selectolax Lexbor
131
+
132
+ Для сайтов с чистым HTML используется движок Lexbor на чистом Си (в 20 раз быстрее BeautifulSoup и потребляет минимум RAM):
133
+
134
+ ```python
135
+ from fast_scraper import parse_html, css_text, css_all_text, css_all_attr
136
+
137
+ tree = parse_html(html_content)
138
+
139
+ # Быстрое извлечение первого найденного текста с авто-strip
140
+ title = css_text(tree, "h1.product-title", default="Без названия")
141
+
142
+ # Извлечение всех элементов
143
+ items = css_all_text(tree, "ul.features li")
144
+
145
+ # Извлечение атрибутов
146
+ image_urls = css_all_attr(tree, "div.gallery img", "src")
147
+ ```
148
+
149
+ ---
150
+
151
+ ### 5. Пул прокси с авто-кулдауном и предотвращением утечек сокетов
152
+
153
+ `fast-scraper` изолирует `AsyncSession` под каждый конкретный запрос/прокси, что гарантирует закрытие Keep-Alive сокетов `libcurl` и предотвращает утечки реального IP при ротации:
154
+
155
+ ```python
156
+ from fast_scraper import ScraperClient, ScraperConfig, ProxyConfig
157
+
158
+ config = ScraperConfig(
159
+ default_impersonate="chrome124",
160
+ timeout_seconds=15.0,
161
+ proxy=ProxyConfig(
162
+ urls=[
163
+ "socks5://127.0.0.1:40000", # Например, локальный Cloudflare WARP
164
+ "socks5://127.0.0.1:40001",
165
+ "http://user:pass@proxy3.net:8080",
166
+ ],
167
+ strategy="least_failed", # "round_robin" | "random" | "least_failed"
168
+ cooldown_seconds=120.0, # Время отстоя прокси при получении 403/429
169
+ max_fails_before_cooldown=2,
170
+ )
171
+ )
172
+
173
+ async with ScraperClient(config=config) as client:
174
+ response = await client.get("https://httpbin.org/ip")
175
+ print(response.json())
176
+ ```
177
+
178
+ ---
179
+
180
+ ## 🏛️ Архитектура пакета
181
+
182
+ ```text
183
+ src/fast_scraper/
184
+ ├── __init__.py # Публичный API
185
+ ├── client.py # ScraperClient с изоляцией сессий и ретраями (tenacity)
186
+ ├── config.py # Pydantic v2 конфигурация (ScraperConfig, ProxyConfig, RetryConfig)
187
+ ├── py.typed # Маркер типизации PEP 561
188
+ ├── extractors/ # Движки извлечения состояний гидратации
189
+ │ ├── nextjs.py # __NEXT_DATA__ и React Server Components (RSC Flight)
190
+ │ ├── meta.py # Threads / Instagram Relay Cache и токены (lsd, app_id)
191
+ │ ├── nuxt.py # Nuxt 2 / 3 (__NUXT_DATA__)
192
+ │ └── generic.py # Schema.org JSON-LD и <script type="application/json">
193
+ ├── dom/ # C-уровень DOM-парсинга
194
+ │ └── parser.py # Обертки над Selectolax Lexbor
195
+ ├── proxy/ # Ротация и мониторинг прокси
196
+ │ ├── manager.py # ProxyManager (Round-Robin, Random, Least-Failed)
197
+ │ └── health.py # Отслеживание сбоев и таймеры кулдауна
198
+ ├── cookies/ # Управление сессиями и куки
199
+ │ ├── memory.py # InMemoryCookieStorage
200
+ │ └── redis_storage.py # RedisCookieStorage
201
+ └── pipeline/ # Трансформация данных
202
+ └── schema_mapper.py # JMESPath -> Pydantic v2 TypeAdapter
203
+ ```
204
+
205
+ ---
206
+
207
+ ## 🧪 Тестирование и верификация
208
+
209
+ Все тесты и проверки запускаются одной командой:
210
+
211
+ ```bash
212
+ # Запуск тестов
213
+ uv run --with pytest --with pytest-asyncio pytest -v
214
+
215
+ # Проверка типов
216
+ uv run --with pyright --with pytest pyright src tests
217
+
218
+ # Проверка линтером
219
+ uv run --with ruff ruff check src tests
220
+ ```
221
+
222
+ ---
223
+
224
+ ## 📄 Лицензия
225
+
226
+ Проект распространяется под свободной лицензией **MIT**. Подробнее см. в файле [LICENSE](LICENSE).
@@ -0,0 +1,103 @@
1
+ import asyncio
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from fast_scraper import (
6
+ ScraperClient,
7
+ css_all_attr,
8
+ css_all_text,
9
+ extract_next_data,
10
+ extract_rsc_flight,
11
+ map_state_to_model,
12
+ parse_html,
13
+ )
14
+
15
+
16
+ # --- Схемы данных Pydantic v2 ---
17
+ class CryptoMetric(BaseModel):
18
+ num_cryptos: int = Field(alias="numCryptocurrencies", description="Всего криптовалют в мире")
19
+ num_markets: int = Field(alias="numMarkets", description="Количество активных рынков")
20
+ active_exchanges: int = Field(alias="activeExchanges", description="Активных бирж")
21
+
22
+
23
+ class HackerNewsItem(BaseModel):
24
+ title: str
25
+ link: str
26
+
27
+
28
+ async def demo_live_nextjs_state():
29
+ print("\n--- [1] РЕАЛЬНЫЙ ЗАПРОС: Next.js SSR гидратация (CoinMarketCap) ---")
30
+ print("Отправка HTTP/2 GET-запроса через curl_cffi с TLS-фингерпринтом Chrome124...")
31
+
32
+ async with ScraperClient() as client:
33
+ response = await client.get("https://coinmarketcap.com/")
34
+ print(f"Статус ответа: {response.status_code}, размер HTML: {len(response.text)} байт")
35
+
36
+ # 1. Извлекаем реальное сырое Next.js состояние без запуска браузера
37
+ state = extract_next_data(response.text)
38
+ print(f"Состояние __NEXT_DATA__ успешно извлечено (ключей в props: {len(state.get('props', {}))})")
39
+
40
+ # 2. Ищем глобальные метрики крипторынка через JMESPath и маппим в Pydantic v2
41
+ metrics = map_state_to_model(
42
+ state=state,
43
+ query="props.dehydratedState.queries[?queryKey[0]=='global-metric'].state.data | [0]",
44
+ model_cls=CryptoMetric,
45
+ strict=False,
46
+ )
47
+
48
+ if isinstance(metrics, CryptoMetric):
49
+ print("\nИзвлеченные в реальном времени данные:")
50
+ print(f" -> Всего криптовалют в листинге: {metrics.num_cryptos:,}")
51
+ print(f" -> Всего активных рынков: {metrics.num_markets:,}")
52
+ print(f" -> Активных бирж: {metrics.active_exchanges}")
53
+
54
+
55
+ async def demo_live_rsc_flight():
56
+ print("\n--- [2] РЕАЛЬНЫЙ ЗАПРОС: React Server Components (Nextjs.org App Router) ---")
57
+ print("Запрос к Next.js сайту с потоковыми RSC Flight чанками (React 19)...")
58
+
59
+ async with ScraperClient() as client:
60
+ response = await client.get("https://nextjs.org/")
61
+ print(f"Статус ответа: {response.status_code}")
62
+
63
+ # Разбор wire-протокола self.__next_f.push
64
+ flight_tree = extract_rsc_flight(response.text)
65
+ print(f"Успешно декодировано {len(flight_tree)} слотов состояния React Server Components!")
66
+
67
+ sample_keys = list(flight_tree.keys())[:4]
68
+ print(f"Примеры идентификаторов слотов: {sample_keys}")
69
+
70
+
71
+ async def demo_live_lexbor_dom():
72
+ print("\n--- [3] РЕАЛЬНЫЙ ЗАПРОС: DOM-парсинг Hacker News через Selectolax Lexbor ---")
73
+
74
+ async with ScraperClient() as client:
75
+ response = await client.get("https://news.ycombinator.com/")
76
+ print(f"Статус ответа: {response.status_code}")
77
+
78
+ tree = parse_html(response.text)
79
+ titles = css_all_text(tree, ".titleline > a")
80
+ links = css_all_attr(tree, ".titleline > a", "href")
81
+
82
+ print(f"Извлечено {len(titles)} актуальных новостей:")
83
+ for idx, (t, link) in enumerate(zip(titles[:5], links[:5], strict=False), 1):
84
+ print(f" {idx}. {t}")
85
+ print(f" URL: {link}")
86
+
87
+
88
+ async def main():
89
+ print("==================================================================")
90
+ print(" РЕАЛЬНАЯ ДЕМОНСТРАЦИЯ: LIVE ПАРСИНГ БЕЗ HEADLESS БРАУЗЕРОВ ")
91
+ print("==================================================================")
92
+
93
+ await demo_live_nextjs_state()
94
+ await demo_live_rsc_flight()
95
+ await demo_live_lexbor_dom()
96
+
97
+ print("\n==================================================================")
98
+ print(" Все РЕАЛЬНЫЕ сетевые сценарии успешно выполнены! ")
99
+ print("==================================================================")
100
+
101
+
102
+ if __name__ == "__main__":
103
+ asyncio.run(main())