flru-parser 0.3.0__py3-none-any.whl
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.
- flru/__init__.py +138 -0
- flru/batch.py +28 -0
- flru/canary.py +33 -0
- flru/client.py +771 -0
- flru/config.py +114 -0
- flru/cookies.py +15 -0
- flru/easy.py +514 -0
- flru/exceptions.py +60 -0
- flru/filters.py +38 -0
- flru/integrations/__init__.py +1 -0
- flru/integrations/opentelemetry.py +29 -0
- flru/integrations/prometheus.py +35 -0
- flru/models.py +297 -0
- flru/observability.py +118 -0
- flru/parsers/__init__.py +13 -0
- flru/parsers/common.py +227 -0
- flru/parsers/freelancers.py +97 -0
- flru/parsers/projects.py +281 -0
- flru/parsers/users.py +193 -0
- flru/proxy.py +87 -0
- flru/py.typed +0 -0
- flru/resilience.py +157 -0
- flru/robots.py +45 -0
- flru/security.py +41 -0
- flru/state.py +324 -0
- flru/sync.py +218 -0
- flru/transport.py +362 -0
- flru_parser-0.3.0.dist-info/METADATA +421 -0
- flru_parser-0.3.0.dist-info/RECORD +33 -0
- flru_parser-0.3.0.dist-info/WHEEL +5 -0
- flru_parser-0.3.0.dist-info/entry_points.txt +2 -0
- flru_parser-0.3.0.dist-info/licenses/LICENSE +21 -0
- flru_parser-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flru-parser
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Simple, typed and resilient async/sync parser client for public FL.ru pages
|
|
5
|
+
Author: Andrey
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/N3ffus/flru-parser#readme
|
|
8
|
+
Project-URL: Changelog, https://github.com/N3ffus/flru-parser/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/N3ffus/flru-parser/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/N3ffus/flru-parser
|
|
11
|
+
Keywords: fl.ru,freelance,parser,scraper,asyncio,httpx
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: beautifulsoup4<5,>=4.12
|
|
27
|
+
Requires-Dist: httpx[http2]<1,>=0.28
|
|
28
|
+
Requires-Dist: lxml<7,>=5
|
|
29
|
+
Requires-Dist: pydantic<3,>=2.10
|
|
30
|
+
Provides-Extra: postgres
|
|
31
|
+
Requires-Dist: asyncpg<1,>=0.30; extra == "postgres"
|
|
32
|
+
Provides-Extra: redis
|
|
33
|
+
Requires-Dist: redis<8,>=5.2; extra == "redis"
|
|
34
|
+
Provides-Extra: observability
|
|
35
|
+
Requires-Dist: opentelemetry-api<2,>=1.29; extra == "observability"
|
|
36
|
+
Requires-Dist: opentelemetry-sdk<2,>=1.29; extra == "observability"
|
|
37
|
+
Requires-Dist: prometheus-client<1,>=0.21; extra == "observability"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# flru-parser
|
|
41
|
+
|
|
42
|
+
[](https://pypi.org/project/flru-parser/)
|
|
43
|
+
[](https://pypi.org/project/flru-parser/)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
[](src/flru/py.typed)
|
|
46
|
+
[](#quality)
|
|
47
|
+
[](#quality)
|
|
48
|
+
[](https://github.com/N3ffus/flru-parser/actions/workflows/ci.yml)
|
|
49
|
+
|
|
50
|
+
A typed, resilient, read-only Python client for public pages on [FL.ru](https://www.fl.ru/).
|
|
51
|
+
The default API is intentionally small; production controls remain available when needed.
|
|
52
|
+
|
|
53
|
+
> **Unofficial project.** This package is not affiliated with or endorsed by FL.ru. FL.ru does not expose a stable public API for all supported data. HTML selectors may require maintenance after a site redesign. The parser detects likely selector drift instead of silently returning an empty catalog.
|
|
54
|
+
|
|
55
|
+
[Русская документация](docs/ru/README.md) · [Simple API](docs/SIMPLE_API.md) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md)
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
- Simple async API: `projects()`, `project()`, `user()`, `freelancers()`, `new_projects()`.
|
|
60
|
+
- Matching synchronous API in `flru.sync`.
|
|
61
|
+
- Projects, details, categories, freelancers, profiles, reviews and portfolios.
|
|
62
|
+
- Typed Pydantic models with PEP 561 support.
|
|
63
|
+
- Retries, jitter, `Retry-After`, shared HTTP 429 cooldown and retry budgets.
|
|
64
|
+
- RPS and concurrency limits, endpoint/proxy circuit breakers and proxy health tracking.
|
|
65
|
+
- HTTP, HTTPS and SOCKS proxy pools with credential redaction.
|
|
66
|
+
- Safe redirects, allowed-host enforcement, `robots.txt` support and block-page detection.
|
|
67
|
+
- Incremental crawling with Memory, SQLite, PostgreSQL or Redis state.
|
|
68
|
+
- Parse diagnostics, confidence, field provenance and page fingerprints.
|
|
69
|
+
- Prometheus, OpenTelemetry and structured event integrations.
|
|
70
|
+
- CI, branch-coverage gate, dependency audit, live canary and PyPI Trusted Publishing.
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv add flru-parser
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Optional integrations:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uv add "flru-parser[postgres]"
|
|
82
|
+
uv add "flru-parser[redis]"
|
|
83
|
+
uv add "flru-parser[observability]"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Supported Python versions: **3.11–3.13**.
|
|
87
|
+
|
|
88
|
+
## Five-minute API
|
|
89
|
+
|
|
90
|
+
### Async
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import asyncio
|
|
94
|
+
|
|
95
|
+
from flru import Client
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def main() -> None:
|
|
99
|
+
async with Client() as fl:
|
|
100
|
+
projects = await fl.projects(pages=5)
|
|
101
|
+
|
|
102
|
+
for project in projects:
|
|
103
|
+
print(project.title, project.budget_min, project.currency, project.url)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
asyncio.run(main())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Sync
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from flru.sync import Client
|
|
113
|
+
|
|
114
|
+
with Client() as fl:
|
|
115
|
+
projects = fl.projects(pages=5)
|
|
116
|
+
|
|
117
|
+
for project in projects:
|
|
118
|
+
print(project.title, project.budget_min, project.url)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### One-shot helpers
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import asyncio
|
|
125
|
+
|
|
126
|
+
from flru import fetch_project, fetch_projects
|
|
127
|
+
|
|
128
|
+
projects = asyncio.run(fetch_projects(pages=3, query="FastAPI"))
|
|
129
|
+
project = asyncio.run(fetch_project(projects[0].id))
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
For synchronous scripts:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from flru.sync import project, projects
|
|
136
|
+
|
|
137
|
+
items = projects(pages=3, query="FastAPI")
|
|
138
|
+
detail = project(items[0].id)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Common operations
|
|
142
|
+
|
|
143
|
+
### Search and filter projects
|
|
144
|
+
|
|
145
|
+
No separate filter object is needed for the common path:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
projects = await fl.projects(
|
|
149
|
+
pages=10,
|
|
150
|
+
query="Python FastAPI",
|
|
151
|
+
category="programmirovanie/python",
|
|
152
|
+
min_budget=30_000,
|
|
153
|
+
max_budget=200_000,
|
|
154
|
+
types="order", # order, vacancy, contest
|
|
155
|
+
with_budget=True,
|
|
156
|
+
concurrency=3,
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`types` accepts one value or a sequence. English and Russian aliases are supported:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
projects = await fl.projects(types=["заказ", "вакансия"])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Parse the complete catalog
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
projects = await fl.projects(pages="all")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
For a large catalog, stream results instead of retaining everything in memory:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
async for project in fl.stream_projects(pages="all", concurrency=3):
|
|
176
|
+
print(project.id, project.title)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Load full project cards
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
details = await fl.projects(pages=3, details=True)
|
|
183
|
+
|
|
184
|
+
for project in details:
|
|
185
|
+
print(project.full_description, project.attachments)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Or retrieve one project by ID or URL:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
project = await fl.project(5500001)
|
|
192
|
+
project = await fl.project("https://www.fl.ru/projects/5500001/example.html")
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Profiles and freelancers
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
profile = await fl.user("username")
|
|
199
|
+
full_profile = await fl.user("username", full=True, pages=3)
|
|
200
|
+
freelancers = await fl.freelancers(pages=5, category="programmirovanie")
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`full=True` concurrently loads projects, reviews and portfolio sections.
|
|
204
|
+
|
|
205
|
+
### Incremental crawling
|
|
206
|
+
|
|
207
|
+
The simplest durable mode uses a local SQLite file automatically:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
new_or_changed = await fl.new_projects(
|
|
211
|
+
"flru-state.db",
|
|
212
|
+
pages=30,
|
|
213
|
+
stop_after_known=20,
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The state tracks content hashes, `first_seen_at`, `last_seen_at` and crawl checkpoints. Repeated runs stop after enough already-known records.
|
|
218
|
+
|
|
219
|
+
Advanced stores remain available:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from flru import PostgresStateStore
|
|
223
|
+
|
|
224
|
+
state = PostgresStateStore("postgresql://user:pass@localhost/app")
|
|
225
|
+
projects = await fl.new_projects(state, pages="all")
|
|
226
|
+
await state.close()
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Proxies, cookies and request limits
|
|
230
|
+
|
|
231
|
+
Common production options are flat constructor arguments:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
async with Client(
|
|
235
|
+
concurrency=3,
|
|
236
|
+
rps=0.8,
|
|
237
|
+
retries=6,
|
|
238
|
+
timeout=45,
|
|
239
|
+
proxy=[
|
|
240
|
+
"http://user:password@proxy-1.example:8080",
|
|
241
|
+
"socks5://user:password@proxy-2.example:1080",
|
|
242
|
+
],
|
|
243
|
+
cookies="cookies.txt",
|
|
244
|
+
) as fl:
|
|
245
|
+
projects = await fl.projects(pages=10)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`cookies` can be a mapping or a Netscape-format browser export path.
|
|
249
|
+
Proxy credentials are redacted from metrics and events.
|
|
250
|
+
|
|
251
|
+
### Models
|
|
252
|
+
|
|
253
|
+
Models are regular Pydantic models:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
project = projects[0]
|
|
257
|
+
|
|
258
|
+
print(project.budget_min)
|
|
259
|
+
print(project.budget_max)
|
|
260
|
+
print(project.currency)
|
|
261
|
+
print(project.customer_username)
|
|
262
|
+
print(project.to_dict())
|
|
263
|
+
print(project.model_dump_json(indent=2))
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Advanced API
|
|
267
|
+
|
|
268
|
+
The simple `Client` subclasses the full `FLClient`, so low-level methods remain directly available:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from flru import FLClient, ProjectFilters
|
|
272
|
+
|
|
273
|
+
async with FLClient() as client:
|
|
274
|
+
page = await client.get_projects_page(page=1)
|
|
275
|
+
batch = await client.get_projects_batch_result(range(1, 11), concurrency=4)
|
|
276
|
+
categories = await client.get_categories()
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Use `FLClient` and the immutable configuration dataclasses when every transport setting must be controlled:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from flru import (
|
|
283
|
+
CircuitBreakerConfig,
|
|
284
|
+
ClientConfig,
|
|
285
|
+
FLClient,
|
|
286
|
+
ProxyConfig,
|
|
287
|
+
RateLimitConfig,
|
|
288
|
+
RetryConfig,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
config = ClientConfig(
|
|
292
|
+
retry=RetryConfig(
|
|
293
|
+
max_attempts=6,
|
|
294
|
+
base_delay=1,
|
|
295
|
+
max_delay=45,
|
|
296
|
+
total_timeout=120,
|
|
297
|
+
max_total_delay=60,
|
|
298
|
+
),
|
|
299
|
+
rate_limit=RateLimitConfig(
|
|
300
|
+
requests_per_second=0.8,
|
|
301
|
+
max_concurrency=4,
|
|
302
|
+
min_interval=0.4,
|
|
303
|
+
),
|
|
304
|
+
circuit_breaker=CircuitBreakerConfig(
|
|
305
|
+
failure_threshold=8,
|
|
306
|
+
recovery_timeout=90,
|
|
307
|
+
scope="endpoint_proxy",
|
|
308
|
+
),
|
|
309
|
+
proxies=ProxyConfig(
|
|
310
|
+
urls=("http://user:password@proxy.example:8080",),
|
|
311
|
+
direct_fallback=True,
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
client = FLClient(config)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
See [docs/SIMPLE_API.md](docs/SIMPLE_API.md) for the complete high-level API and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for internals.
|
|
319
|
+
|
|
320
|
+
## Reliability behavior
|
|
321
|
+
|
|
322
|
+
- Empty output is not automatically treated as the end of the catalog.
|
|
323
|
+
- Candidate links with no parsed cards raise `SelectorDriftError`.
|
|
324
|
+
- Unclassified empty pages raise `EmptyPageError`.
|
|
325
|
+
- A recognized end page contains the `catalog_end` diagnostic warning.
|
|
326
|
+
- HTTP 429 can pause all concurrent workers through a shared cooldown.
|
|
327
|
+
- Circuit breakers are scoped by endpoint and proxy by default.
|
|
328
|
+
- CAPTCHA and block pages are detected, not bypassed.
|
|
329
|
+
- Requests are restricted to allowed FL.ru hosts and safe redirects.
|
|
330
|
+
|
|
331
|
+
## Observability
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
metrics = await fl.stats()
|
|
335
|
+
print(metrics.requests_total, metrics.retries_total, metrics.endpoints)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Structured logging:
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
from flru import Client, StructuredLogHandler
|
|
342
|
+
|
|
343
|
+
fl = Client(event_handler=StructuredLogHandler())
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Optional adapters:
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
from flru.integrations.opentelemetry import OpenTelemetryEventHandler
|
|
350
|
+
from flru.integrations.prometheus import PrometheusEventHandler
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Install `flru-parser[observability]` before using these adapters.
|
|
354
|
+
|
|
355
|
+
## Exceptions
|
|
356
|
+
|
|
357
|
+
| Exception | Meaning |
|
|
358
|
+
|---|---|
|
|
359
|
+
| `BlockedError` | CAPTCHA, anti-bot or access-block page detected |
|
|
360
|
+
| `AuthenticationRequired` | Authenticated cookies are required |
|
|
361
|
+
| `RateLimitedError` | HTTP 429 after retry policy exhaustion |
|
|
362
|
+
| `CircuitOpenError` | A scoped circuit breaker is open |
|
|
363
|
+
| `SecurityError` | Host, scheme or redirect violates policy |
|
|
364
|
+
| `RobotsDeniedError` | `robots.txt` denies access |
|
|
365
|
+
| `SelectorDriftError` | Candidate records exist but current selectors parsed none |
|
|
366
|
+
| `EmptyPageError` | Empty page cannot be classified as catalog end |
|
|
367
|
+
| `ParseError` | HTML cannot be converted into the requested model |
|
|
368
|
+
|
|
369
|
+
## Quality
|
|
370
|
+
|
|
371
|
+
Current verified result for **0.3.0**:
|
|
372
|
+
|
|
373
|
+
```text
|
|
374
|
+
47 passed, 1 optional Hypothesis suite skipped locally
|
|
375
|
+
Branch coverage: 85.76%
|
|
376
|
+
Coverage gate: 85%
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Run the complete local checks:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
uv sync --group dev
|
|
383
|
+
uv run ruff check .
|
|
384
|
+
uv run ruff format --check .
|
|
385
|
+
uv run mypy src/flru
|
|
386
|
+
uv run pytest --cov=flru --cov-branch --cov-report=term-missing --cov-report=html
|
|
387
|
+
uv build
|
|
388
|
+
uvx twine check dist/*
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The repository also contains:
|
|
392
|
+
|
|
393
|
+
- Python 3.11–3.13 CI matrix;
|
|
394
|
+
- scheduled dependency vulnerability audit;
|
|
395
|
+
- read-only live selector canary;
|
|
396
|
+
- pre-commit configuration;
|
|
397
|
+
- Trusted Publishing workflows for TestPyPI and PyPI.
|
|
398
|
+
|
|
399
|
+
## Publishing
|
|
400
|
+
|
|
401
|
+
The release workflow uses PyPI Trusted Publishing and checks that the Git tag matches both package version declarations.
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
uv run python scripts/configure_project.py YOUR_GITHUB_USERNAME
|
|
405
|
+
git tag -a v0.3.0 -m "flru-parser 0.3.0"
|
|
406
|
+
git push origin main v0.3.0
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
See [docs/PUBLISHING.md](docs/PUBLISHING.md) for the one-time PyPI and GitHub setup.
|
|
410
|
+
|
|
411
|
+
## Responsible use
|
|
412
|
+
|
|
413
|
+
- Respect FL.ru terms, `robots.txt`, applicable law and personal-data requirements.
|
|
414
|
+
- Prefer incremental crawling and reasonable request rates.
|
|
415
|
+
- Do not use proxies or this package to bypass access controls or CAPTCHA.
|
|
416
|
+
- Do not automate account actions through undocumented endpoints.
|
|
417
|
+
- Public indexability does not automatically grant unlimited collection or redistribution rights.
|
|
418
|
+
|
|
419
|
+
## License
|
|
420
|
+
|
|
421
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
flru/__init__.py,sha256=lFrVs-PRWp67zd5EOLaJN2yxFfl-2g8m-lkg3gmhQ9I,2704
|
|
2
|
+
flru/batch.py,sha256=kYDgVJJf-1gy64T5PxPqQYIB0q2fJDz8UbJ3RsQomjk,625
|
|
3
|
+
flru/canary.py,sha256=ENeuztMB4G6P355nBUoLyUAy3o84lwyzD1nfj64AmZM,914
|
|
4
|
+
flru/client.py,sha256=fE_xCjB61nT7sXd9uY4MKKGipP6xi5_CkpaoeQ0Vb_4,30376
|
|
5
|
+
flru/config.py,sha256=-xLydcgEY-z7lMCZTvRcsW8kKaFBIFifUzgPyuf6ZE0,4226
|
|
6
|
+
flru/cookies.py,sha256=pov7XZMZyEJGmlMiRCfB7GyAoDiEGxS2tf7LAeEJBKo,500
|
|
7
|
+
flru/easy.py,sha256=6vKsYCVlPOH8Y_NJcn9joyTJPnHLj5k_qmQWsxppPeA,16990
|
|
8
|
+
flru/exceptions.py,sha256=aWzFv4lHIZXMoAH_jl7Vt1Y-KpEGh6QPZyQVTMz57Og,1598
|
|
9
|
+
flru/filters.py,sha256=07Zm8XCQP31B3IRl3Wf8L4aEGc8IQ4WYQVvQfpxp_8Q,1251
|
|
10
|
+
flru/models.py,sha256=eEO9KXEhSdpdVirf28Pe1N9ZucZR-hnlGujp-T-DFyg,8474
|
|
11
|
+
flru/observability.py,sha256=NyWZuJPEs8gor9Uln2JjxHG77QPqWhi3RSxwJn3mxkM,3960
|
|
12
|
+
flru/proxy.py,sha256=lklKtZu_GO35VPOz-ocPWgrRkSw39MzoXaEn2R7z9M4,2878
|
|
13
|
+
flru/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
flru/resilience.py,sha256=a5WH6ljiFzjKAME5j4PNIf5pJ_oWRl3JahIpPWuatTk,5387
|
|
15
|
+
flru/robots.py,sha256=CTQPe4BCm-SxUHZt73fcqWTmmwNulBj2ox3ccIpyO08,1973
|
|
16
|
+
flru/security.py,sha256=WFBF98hnlefSwlLnLuhXSSEEw5vlJlMw9MNi40BUHRM,1455
|
|
17
|
+
flru/state.py,sha256=8T337jecGKScKv_IU4Sf1S4-0gQgsLlroa1hNUFBotA,12383
|
|
18
|
+
flru/sync.py,sha256=u3rJt2cbnnEWtdfHMm0J2Kef1cmU8JgcsexDme7XOJE,6710
|
|
19
|
+
flru/transport.py,sha256=SBtN2ZP1NwXgdItOgiBbrEbN8CwBArlJVzrzuyyaB9k,14025
|
|
20
|
+
flru/integrations/__init__.py,sha256=cXG129B1iZSQ3O39LJART1PFK_3p6abMMrxoQEJeCmY,43
|
|
21
|
+
flru/integrations/opentelemetry.py,sha256=0-WGJPFnuON5Te21ZFNa1TIVdHu974MgK2KsFp6aKHE,990
|
|
22
|
+
flru/integrations/prometheus.py,sha256=JYRoM2NFYcDWQzGQ0RHqLdhGpEE7pBGe8pn1EoFBnkQ,1198
|
|
23
|
+
flru/parsers/__init__.py,sha256=lEFj6Be5gHHvaCtWQFH0BiRoVzyGnVoOQczCNTwfvwQ,365
|
|
24
|
+
flru/parsers/common.py,sha256=_Uef1_yPorBSr-9NXoGaIYmaIYnsopoKz-N1ovkF1NQ,9270
|
|
25
|
+
flru/parsers/freelancers.py,sha256=Ukq2UwZGQiknuKNGEexULTROi4Hi-agvDHAatYvwIbQ,4936
|
|
26
|
+
flru/parsers/projects.py,sha256=PwmkU2RHF1Ow28Xfo4tTjkKOFb-_RWNOPPGdLGCA9CI,13414
|
|
27
|
+
flru/parsers/users.py,sha256=HRBftGXW3Ju1TKZZJtQJ833_eRkg0Jnu0pMcGN-8JXE,7380
|
|
28
|
+
flru_parser-0.3.0.dist-info/licenses/LICENSE,sha256=1zNNV91mmCQYfzAOrjvPh0c18NR6BImrUBS18w3yvFc,1063
|
|
29
|
+
flru_parser-0.3.0.dist-info/METADATA,sha256=3m4FQqjRQ9dKHn7HWAXN6Uic5jHyzjICdXnkLQ_jZa8,12430
|
|
30
|
+
flru_parser-0.3.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
31
|
+
flru_parser-0.3.0.dist-info/entry_points.txt,sha256=pA1nzHVJnt6IjNusyCW8paiu4oUbRfTwzp669owuEAY,49
|
|
32
|
+
flru_parser-0.3.0.dist-info/top_level.txt,sha256=1oNJlhBZ1TyECwrj5InG0Kfi1P_KeEwJO2ASSBD-9uw,5
|
|
33
|
+
flru_parser-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Andrey
|
|
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 @@
|
|
|
1
|
+
flru
|