scrapeflow-py 0.3.0__tar.gz → 0.4.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 (60) hide show
  1. scrapeflow_py-0.4.0/CHANGELOG.md +53 -0
  2. scrapeflow_py-0.4.0/MANIFEST.in +5 -0
  3. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/PKG-INFO +73 -31
  4. scrapeflow_py-0.3.0/scrapeflow_py.egg-info/PKG-INFO → scrapeflow_py-0.4.0/README.md +43 -47
  5. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/__init__.py +0 -1
  6. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/advanced_example.py +5 -6
  7. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/authenticated_quotes_example.py +2 -1
  8. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/basic_usage.py +2 -1
  9. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/hybrid_extraction_example.py +8 -5
  10. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/llm_extraction_example.py +5 -8
  11. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/session_persistence_example.py +9 -3
  12. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/specification_driven_example.py +6 -4
  13. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/examples/workflow_example.py +4 -4
  14. scrapeflow_py-0.4.0/pyproject.toml +107 -0
  15. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/__init__.py +36 -30
  16. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/anti_detection.py +18 -31
  17. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/browser_runtime.py +9 -9
  18. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/config.py +13 -19
  19. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/content_utils.py +0 -1
  20. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/engine.py +41 -40
  21. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/exceptions.py +0 -1
  22. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/extractors.py +19 -29
  23. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/llm_extract.py +24 -27
  24. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/mcp_backend.py +36 -26
  25. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/monitoring.py +13 -14
  26. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/pagination.py +8 -3
  27. scrapeflow_py-0.4.0/scrapeflow/ports.py +57 -0
  28. scrapeflow_py-0.4.0/scrapeflow/py.typed +0 -0
  29. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/rate_limiter.py +4 -9
  30. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/registry.py +20 -20
  31. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/retry.py +9 -19
  32. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/robots.py +1 -4
  33. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/specifications.py +34 -33
  34. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/workflow.py +18 -18
  35. scrapeflow_py-0.3.0/README.md → scrapeflow_py-0.4.0/scrapeflow_py.egg-info/PKG-INFO +90 -12
  36. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow_py.egg-info/SOURCES.txt +3 -3
  37. scrapeflow_py-0.4.0/scrapeflow_py.egg-info/requires.txt +15 -0
  38. scrapeflow_py-0.4.0/scrapeflow_py.egg-info/top_level.txt +1 -0
  39. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_config.py +5 -7
  40. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_content_utils.py +0 -1
  41. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_hybrid_extractor.py +9 -6
  42. scrapeflow_py-0.4.0/tests/test_release.py +207 -0
  43. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_robots.py +1 -0
  44. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_specifications.py +1 -5
  45. scrapeflow_py-0.3.0/MANIFEST.in +0 -4
  46. scrapeflow_py-0.3.0/pyproject.toml +0 -16
  47. scrapeflow_py-0.3.0/requirements.txt +0 -7
  48. scrapeflow_py-0.3.0/scrapeflow/ports.py +0 -71
  49. scrapeflow_py-0.3.0/scrapeflow_py.egg-info/requires.txt +0 -6
  50. scrapeflow_py-0.3.0/scrapeflow_py.egg-info/top_level.txt +0 -3
  51. scrapeflow_py-0.3.0/setup.py +0 -38
  52. scrapeflow_py-0.3.0/tests/__init__.py +0 -2
  53. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/LICENSE +0 -0
  54. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/schema_library.py +0 -0
  55. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow/workflow_executor.py +0 -0
  56. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/scrapeflow_py.egg-info/dependency_links.txt +0 -0
  57. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/setup.cfg +0 -0
  58. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_engine_orchestration.py +0 -0
  59. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_public_api_compat.py +0 -0
  60. {scrapeflow_py-0.3.0 → scrapeflow_py-0.4.0}/tests/test_registry.py +0 -0
@@ -0,0 +1,53 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.4.0] - 2026-10-31
11
+
12
+ ### Fixed
13
+ - LLM extraction works with current `mistralai` releases (2.x/3.x moved the client to
14
+ `mistralai.client.Mistral`). Previously every LLM call failed with a misleading
15
+ "mistralai is required" `ImportError` even though it was installed.
16
+ - The wheel no longer installs stray top-level `tests` and `examples` packages into
17
+ `site-packages`.
18
+ - `create_mcp_backend()` no longer silently returns a do-nothing backend: unknown backend names
19
+ raise `ValueError`, and the not-yet-implemented `scrapy_mcp` / `playwright_mcp` backends emit a
20
+ `UserWarning`.
21
+ - Step timeouts chain the original `asyncio.TimeoutError` (`raise ... from`).
22
+ - Dockerfile: browsers are installed where `PLAYWRIGHT_BROWSERS_PATH` points, and `pytest` is
23
+ available for the default `CMD` (the image previously could not run its own default command).
24
+
25
+ ### Added
26
+ - `py.typed` marker (PEP 561) and `Typing :: Typed` classifier.
27
+ - `__version__` is read from installed package metadata (`importlib.metadata`).
28
+ - Tests: mocked Mistral schema generation and extraction (code-fence handling, bad JSON, async,
29
+ new SDK layout), MCP backend factory, and real-browser tests against a local HTTP server
30
+ (spec-driven extraction and robots.txt enforcement). 36 tests total.
31
+ - CHANGELOG, SECURITY.md, CODE_OF_CONDUCT.md, issue/PR templates, Dependabot, CITATION.cff.
32
+
33
+ ### Changed
34
+ - Requires Python 3.10+ (3.8 and 3.9 are end-of-life); classifiers for 3.10 to 3.13.
35
+ - Packaging is `pyproject.toml`-only (removed `setup.py`, `pytest.ini`, `requirements*.txt`),
36
+ with PEP 639 `license = "MIT"` and a `dev` extra (pytest, black, ruff, mypy, build, twine).
37
+ - CI: lint (ruff + black) is enforced instead of `|| true`, tests run on Python 3.10 to 3.13 with
38
+ Chromium installed, and the Docker job now runs the test suite inside the image.
39
+ - Codebase formatted with black and cleaned with ruff (imports, modern type hints).
40
+
41
+ ## [0.3.0] - 2026-03-09
42
+
43
+ ### Added
44
+ - Hybrid extraction, Mistral LLM extraction, session persistence, content cleaning.
45
+
46
+ ## [0.2.0] - 2026-03-09
47
+
48
+ ### Added
49
+ - Specification-driven extraction, robots.txt compliance, ethical crawling, architecture refactor.
50
+
51
+ ## [0.1.2] - 2025-11-27
52
+
53
+ - Initial public releases (0.1.0 to 0.1.2).
@@ -0,0 +1,5 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CHANGELOG.md
4
+ include scrapeflow/py.typed
5
+ recursive-include examples *.py
@@ -1,20 +1,31 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scrapeflow-py
3
- Version: 0.3.0
4
- Summary: An opinionated scraping workflow engine built on Playwright
5
- Home-page: https://github.com/irfanalidv/scrapeflow-py
6
- Author: Irfan Ali
7
- License: MIT
8
- Classifier: Development Status :: 3 - Alpha
3
+ Version: 0.4.0
4
+ Summary: Opinionated Playwright scraping workflow engine: retries, rate limiting, anti-detection, robots.txt compliance, Pydantic spec-driven and LLM-powered extraction, sessions, and metrics.
5
+ Author-email: Irfan Ali <irfanali29@hotmail.com>
6
+ Maintainer-email: Irfan Ali <irfanali29@hotmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/irfanalidv/scrapeflow-py
9
+ Project-URL: Documentation, https://github.com/irfanalidv/scrapeflow-py#readme
10
+ Project-URL: Repository, https://github.com/irfanalidv/scrapeflow-py
11
+ Project-URL: Issues, https://github.com/irfanalidv/scrapeflow-py/issues
12
+ Project-URL: Changelog, https://github.com/irfanalidv/scrapeflow-py/blob/main/CHANGELOG.md
13
+ Keywords: web-scraping,scraping,scraper,crawler,playwright,browser-automation,workflow,data-extraction,llm-extraction,mistral,pydantic,rate-limiting,retry,robots-txt,anti-detection,asyncio
14
+ Classifier: Development Status :: 4 - Beta
9
15
  Classifier: Intended Audience :: Developers
10
- Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Framework :: AsyncIO
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
21
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.8
13
- Classifier: Programming Language :: Python :: 3.9
14
22
  Classifier: Programming Language :: Python :: 3.10
15
23
  Classifier: Programming Language :: Python :: 3.11
16
24
  Classifier: Programming Language :: Python :: 3.12
17
- Requires-Python: >=3.8
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3 :: Only
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.10
18
29
  Description-Content-Type: text/markdown
19
30
  License-File: LICENSE
20
31
  Requires-Dist: playwright>=1.40.0
@@ -23,22 +34,34 @@ Requires-Dist: tenacity>=8.2.0
23
34
  Requires-Dist: pydantic>=2.0.0
24
35
  Requires-Dist: mistralai>=1.0.0
25
36
  Requires-Dist: python-dotenv>=1.0.0
26
- Dynamic: author
27
- Dynamic: classifier
28
- Dynamic: description
29
- Dynamic: description-content-type
30
- Dynamic: home-page
31
- Dynamic: license
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
39
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
40
+ Requires-Dist: black>=24.0.0; extra == "dev"
41
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
42
+ Requires-Dist: mypy>=1.7.0; extra == "dev"
43
+ Requires-Dist: build>=1.0.0; extra == "dev"
44
+ Requires-Dist: twine>=5.0.0; extra == "dev"
32
45
  Dynamic: license-file
33
- Dynamic: requires-dist
34
- Dynamic: requires-python
35
- Dynamic: summary
46
+
47
+ <div align="center">
36
48
 
37
49
  # ScrapeFlow
38
50
 
39
- **An opinionated scraping workflow engine built on Playwright**
51
+ **An opinionated scraping workflow engine built on Playwright.**
52
+
53
+ Retries, rate limiting, anti-detection, robots.txt compliance, Pydantic spec-driven and
54
+ Mistral LLM-powered extraction, login sessions, workflows, and metrics in one async API.
55
+
56
+ [![PyPI version](https://img.shields.io/pypi/v/scrapeflow-py.svg)](https://pypi.org/project/scrapeflow-py/)
57
+ [![Python versions](https://img.shields.io/pypi/pyversions/scrapeflow-py.svg)](https://pypi.org/project/scrapeflow-py/)
58
+ [![CI](https://github.com/irfanalidv/scrapeflow-py/actions/workflows/ci.yml/badge.svg)](https://github.com/irfanalidv/scrapeflow-py/actions/workflows/ci.yml)
59
+ [![PyPI Downloads](https://static.pepy.tech/badge/scrapeflow-py)](https://pepy.tech/projects/scrapeflow-py)
60
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/irfanalidv/scrapeflow-py/blob/main/LICENSE)
61
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
62
+ [![Playwright](https://img.shields.io/badge/Playwright-1.40%2B-green)](https://playwright.dev/)
40
63
 
41
- [![GitHub](https://img.shields.io/github/license/irfanalidv/scrapeflow-py)](https://github.com/irfanalidv/scrapeflow-py/blob/main/LICENSE) [![PyPI](https://img.shields.io/pypi/v/scrapeflow-py)](https://pypi.org/project/scrapeflow-py/) [![PyPI Downloads](https://static.pepy.tech/personalized-badge/scrapeflow-py?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/scrapeflow-py) [![Python](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/) [![Playwright](https://img.shields.io/badge/Playwright-1.40%2B-green)](https://playwright.dev/) [![Status](https://img.shields.io/badge/status-active-success)](https://github.com/irfanalidv/scrapeflow-py)
64
+ </div>
42
65
 
43
66
  ---
44
67
 
@@ -51,7 +74,7 @@ ScrapeFlow is a production-ready Python library that transforms Playwright into
51
74
  - **⚖️ Ethical Crawling (GDPR/CCPA)**: Configurable data retention, anonymization, and consent options in the specification layer
52
75
  - **📦 Component Registry**: Shared, versioned selectors, pagination handlers, and login flows—platform thinking over one-off scrapers
53
76
  - **🔔 Monitoring & Alerting**: Alert callbacks on failure thresholds; rollback hooks for failed extraction runs
54
- - **🔌 MCP Extensibility**: Pluggable backends for Scrapy MCP Server, Playwright MCP, or LLM-based semantic extraction
77
+ - **🔌 Pluggable Extraction Backends**: `MCPBackend` interface with a built-in Mistral LLM backend; subclass it to plug in an MCP server (Scrapy/Playwright MCP adapters are planned, not yet implemented)
55
78
  - **🤖 Mistral LLM Extraction**: Generate schemas from natural language, extract without selectors—uses `MISTRAL_API_KEY`
56
79
  - **🔄 Hybrid Extraction**: Selector-first, LLM fallback when validation fails—self-healing spiders
57
80
  - **🛡️ Selector Fallback Chain**: Try multiple selectors per field (`[".price", ".cost"]`) for resilience
@@ -67,13 +90,16 @@ ScrapeFlow is a production-ready Python library that transforms Playwright into
67
90
  - **📝 Type Hints**: Full type support for better IDE experience
68
91
 
69
92
  <p align="center">
70
- <img src="docs/scrapeflow-architecture.png" alt="ScrapeFlow Architecture" width="800">
93
+ <img src="https://raw.githubusercontent.com/irfanalidv/scrapeflow-py/main/docs/scrapeflow-architecture.png" alt="ScrapeFlow Architecture" width="800">
71
94
  </p>
72
95
 
73
96
  ## 📦 Installation
74
97
 
98
+ Requires Python 3.10+.
99
+
75
100
  ```bash
76
101
  pip install scrapeflow-py
102
+ playwright install chromium # download the browser Playwright drives
77
103
  ```
78
104
 
79
105
  Or install from source:
@@ -82,17 +108,24 @@ Or install from source:
82
108
  git clone https://github.com/irfanalidv/scrapeflow-py.git
83
109
  cd scrapeflow-py
84
110
  pip install -e .
111
+ playwright install chromium
85
112
  ```
86
113
 
87
- **Note**: After installation, install Playwright browsers:
114
+ On a fresh Linux machine or container, `playwright install --with-deps chromium` also installs
115
+ the system libraries Chromium needs. A `Dockerfile` is included.
88
116
 
89
- ```bash
90
- playwright install
91
- ```
117
+ ## ⚙️ Configuration (environment variables)
118
+
119
+ | Variable | Purpose |
120
+ | --- | --- |
121
+ | `MISTRAL_API_KEY` | Required only for LLM features (`generate_schema_from_prompt`, `extract_with_schema`, `MistralLLMBackend`, `HybridExtractor` LLM fallback). Read from the environment or a `.env` file. |
122
+
123
+ Everything else (rate limits, retries, browser, anti-detection, robots.txt, ethical crawling) is
124
+ configured in code via `ScrapeFlowConfig`; see [Configuration](#configuration) below.
92
125
 
93
126
  ## 🎯 Real-World Use Cases
94
127
 
95
- ScrapeFlow is used in production for:
128
+ Typical use cases:
96
129
 
97
130
  - **💰 E-commerce Price Monitoring** - Track competitor prices, monitor deals, and optimize pricing strategies
98
131
  - **📰 News & Content Aggregation** - Collect articles from multiple sources for content platforms
@@ -1247,11 +1280,20 @@ scrapeflow/
1247
1280
  └── exceptions.py # Custom exceptions
1248
1281
  ```
1249
1282
 
1250
- For deeper design details, see [`ARCHITECTURE.md`](ARCHITECTURE.md).
1283
+ For deeper design details, see [`ARCHITECTURE.md`](https://github.com/irfanalidv/scrapeflow-py/blob/main/ARCHITECTURE.md). Release notes are in [`CHANGELOG.md`](https://github.com/irfanalidv/scrapeflow-py/blob/main/CHANGELOG.md).
1251
1284
 
1252
1285
  ## 🤝 Contributing
1253
1286
 
1254
- Contributions are welcome! Please feel free to submit a Pull Request.
1287
+ Contributions are welcome! See [CONTRIBUTING.md](https://github.com/irfanalidv/scrapeflow-py/blob/main/CONTRIBUTING.md)
1288
+ and the [Code of Conduct](https://github.com/irfanalidv/scrapeflow-py/blob/main/CODE_OF_CONDUCT.md).
1289
+
1290
+ ```bash
1291
+ git clone https://github.com/irfanalidv/scrapeflow-py.git && cd scrapeflow-py
1292
+ pip install -e ".[dev]" && playwright install chromium
1293
+ ruff check . && black --check . && pytest
1294
+ ```
1295
+
1296
+ Security issues: please follow [SECURITY.md](https://github.com/irfanalidv/scrapeflow-py/blob/main/SECURITY.md).
1255
1297
 
1256
1298
  1. Fork the repository
1257
1299
  2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
@@ -1261,7 +1303,7 @@ Contributions are welcome! Please feel free to submit a Pull Request.
1261
1303
 
1262
1304
  ## 📝 License
1263
1305
 
1264
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
1306
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/irfanalidv/scrapeflow-py/blob/main/LICENSE) file for details.
1265
1307
 
1266
1308
  ## 🙏 Acknowledgments
1267
1309
 
@@ -1,44 +1,21 @@
1
- Metadata-Version: 2.4
2
- Name: scrapeflow-py
3
- Version: 0.3.0
4
- Summary: An opinionated scraping workflow engine built on Playwright
5
- Home-page: https://github.com/irfanalidv/scrapeflow-py
6
- Author: Irfan Ali
7
- License: MIT
8
- Classifier: Development Status :: 3 - Alpha
9
- Classifier: Intended Audience :: Developers
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.8
13
- Classifier: Programming Language :: Python :: 3.9
14
- Classifier: Programming Language :: Python :: 3.10
15
- Classifier: Programming Language :: Python :: 3.11
16
- Classifier: Programming Language :: Python :: 3.12
17
- Requires-Python: >=3.8
18
- Description-Content-Type: text/markdown
19
- License-File: LICENSE
20
- Requires-Dist: playwright>=1.40.0
21
- Requires-Dist: aiohttp>=3.9.0
22
- Requires-Dist: tenacity>=8.2.0
23
- Requires-Dist: pydantic>=2.0.0
24
- Requires-Dist: mistralai>=1.0.0
25
- Requires-Dist: python-dotenv>=1.0.0
26
- Dynamic: author
27
- Dynamic: classifier
28
- Dynamic: description
29
- Dynamic: description-content-type
30
- Dynamic: home-page
31
- Dynamic: license
32
- Dynamic: license-file
33
- Dynamic: requires-dist
34
- Dynamic: requires-python
35
- Dynamic: summary
1
+ <div align="center">
36
2
 
37
3
  # ScrapeFlow
38
4
 
39
- **An opinionated scraping workflow engine built on Playwright**
5
+ **An opinionated scraping workflow engine built on Playwright.**
40
6
 
41
- [![GitHub](https://img.shields.io/github/license/irfanalidv/scrapeflow-py)](https://github.com/irfanalidv/scrapeflow-py/blob/main/LICENSE) [![PyPI](https://img.shields.io/pypi/v/scrapeflow-py)](https://pypi.org/project/scrapeflow-py/) [![PyPI Downloads](https://static.pepy.tech/personalized-badge/scrapeflow-py?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/scrapeflow-py) [![Python](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/) [![Playwright](https://img.shields.io/badge/Playwright-1.40%2B-green)](https://playwright.dev/) [![Status](https://img.shields.io/badge/status-active-success)](https://github.com/irfanalidv/scrapeflow-py)
7
+ Retries, rate limiting, anti-detection, robots.txt compliance, Pydantic spec-driven and
8
+ Mistral LLM-powered extraction, login sessions, workflows, and metrics in one async API.
9
+
10
+ [![PyPI version](https://img.shields.io/pypi/v/scrapeflow-py.svg)](https://pypi.org/project/scrapeflow-py/)
11
+ [![Python versions](https://img.shields.io/pypi/pyversions/scrapeflow-py.svg)](https://pypi.org/project/scrapeflow-py/)
12
+ [![CI](https://github.com/irfanalidv/scrapeflow-py/actions/workflows/ci.yml/badge.svg)](https://github.com/irfanalidv/scrapeflow-py/actions/workflows/ci.yml)
13
+ [![PyPI Downloads](https://static.pepy.tech/badge/scrapeflow-py)](https://pepy.tech/projects/scrapeflow-py)
14
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/irfanalidv/scrapeflow-py/blob/main/LICENSE)
15
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
16
+ [![Playwright](https://img.shields.io/badge/Playwright-1.40%2B-green)](https://playwright.dev/)
17
+
18
+ </div>
42
19
 
43
20
  ---
44
21
 
@@ -51,7 +28,7 @@ ScrapeFlow is a production-ready Python library that transforms Playwright into
51
28
  - **⚖️ Ethical Crawling (GDPR/CCPA)**: Configurable data retention, anonymization, and consent options in the specification layer
52
29
  - **📦 Component Registry**: Shared, versioned selectors, pagination handlers, and login flows—platform thinking over one-off scrapers
53
30
  - **🔔 Monitoring & Alerting**: Alert callbacks on failure thresholds; rollback hooks for failed extraction runs
54
- - **🔌 MCP Extensibility**: Pluggable backends for Scrapy MCP Server, Playwright MCP, or LLM-based semantic extraction
31
+ - **🔌 Pluggable Extraction Backends**: `MCPBackend` interface with a built-in Mistral LLM backend; subclass it to plug in an MCP server (Scrapy/Playwright MCP adapters are planned, not yet implemented)
55
32
  - **🤖 Mistral LLM Extraction**: Generate schemas from natural language, extract without selectors—uses `MISTRAL_API_KEY`
56
33
  - **🔄 Hybrid Extraction**: Selector-first, LLM fallback when validation fails—self-healing spiders
57
34
  - **🛡️ Selector Fallback Chain**: Try multiple selectors per field (`[".price", ".cost"]`) for resilience
@@ -67,13 +44,16 @@ ScrapeFlow is a production-ready Python library that transforms Playwright into
67
44
  - **📝 Type Hints**: Full type support for better IDE experience
68
45
 
69
46
  <p align="center">
70
- <img src="docs/scrapeflow-architecture.png" alt="ScrapeFlow Architecture" width="800">
47
+ <img src="https://raw.githubusercontent.com/irfanalidv/scrapeflow-py/main/docs/scrapeflow-architecture.png" alt="ScrapeFlow Architecture" width="800">
71
48
  </p>
72
49
 
73
50
  ## 📦 Installation
74
51
 
52
+ Requires Python 3.10+.
53
+
75
54
  ```bash
76
55
  pip install scrapeflow-py
56
+ playwright install chromium # download the browser Playwright drives
77
57
  ```
78
58
 
79
59
  Or install from source:
@@ -82,17 +62,24 @@ Or install from source:
82
62
  git clone https://github.com/irfanalidv/scrapeflow-py.git
83
63
  cd scrapeflow-py
84
64
  pip install -e .
65
+ playwright install chromium
85
66
  ```
86
67
 
87
- **Note**: After installation, install Playwright browsers:
68
+ On a fresh Linux machine or container, `playwright install --with-deps chromium` also installs
69
+ the system libraries Chromium needs. A `Dockerfile` is included.
88
70
 
89
- ```bash
90
- playwright install
91
- ```
71
+ ## ⚙️ Configuration (environment variables)
72
+
73
+ | Variable | Purpose |
74
+ | --- | --- |
75
+ | `MISTRAL_API_KEY` | Required only for LLM features (`generate_schema_from_prompt`, `extract_with_schema`, `MistralLLMBackend`, `HybridExtractor` LLM fallback). Read from the environment or a `.env` file. |
76
+
77
+ Everything else (rate limits, retries, browser, anti-detection, robots.txt, ethical crawling) is
78
+ configured in code via `ScrapeFlowConfig`; see [Configuration](#configuration) below.
92
79
 
93
80
  ## 🎯 Real-World Use Cases
94
81
 
95
- ScrapeFlow is used in production for:
82
+ Typical use cases:
96
83
 
97
84
  - **💰 E-commerce Price Monitoring** - Track competitor prices, monitor deals, and optimize pricing strategies
98
85
  - **📰 News & Content Aggregation** - Collect articles from multiple sources for content platforms
@@ -1247,11 +1234,20 @@ scrapeflow/
1247
1234
  └── exceptions.py # Custom exceptions
1248
1235
  ```
1249
1236
 
1250
- For deeper design details, see [`ARCHITECTURE.md`](ARCHITECTURE.md).
1237
+ For deeper design details, see [`ARCHITECTURE.md`](https://github.com/irfanalidv/scrapeflow-py/blob/main/ARCHITECTURE.md). Release notes are in [`CHANGELOG.md`](https://github.com/irfanalidv/scrapeflow-py/blob/main/CHANGELOG.md).
1251
1238
 
1252
1239
  ## 🤝 Contributing
1253
1240
 
1254
- Contributions are welcome! Please feel free to submit a Pull Request.
1241
+ Contributions are welcome! See [CONTRIBUTING.md](https://github.com/irfanalidv/scrapeflow-py/blob/main/CONTRIBUTING.md)
1242
+ and the [Code of Conduct](https://github.com/irfanalidv/scrapeflow-py/blob/main/CODE_OF_CONDUCT.md).
1243
+
1244
+ ```bash
1245
+ git clone https://github.com/irfanalidv/scrapeflow-py.git && cd scrapeflow-py
1246
+ pip install -e ".[dev]" && playwright install chromium
1247
+ ruff check . && black --check . && pytest
1248
+ ```
1249
+
1250
+ Security issues: please follow [SECURITY.md](https://github.com/irfanalidv/scrapeflow-py/blob/main/SECURITY.md).
1255
1251
 
1256
1252
  1. Fork the repository
1257
1253
  2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
@@ -1261,7 +1257,7 @@ Contributions are welcome! Please feel free to submit a Pull Request.
1261
1257
 
1262
1258
  ## 📝 License
1263
1259
 
1264
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
1260
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/irfanalidv/scrapeflow-py/blob/main/LICENSE) file for details.
1265
1261
 
1266
1262
  ## 🙏 Acknowledgments
1267
1263
 
@@ -1,2 +1 @@
1
1
  """Examples for ScrapeFlow usage."""
2
-
@@ -1,14 +1,15 @@
1
1
  """Advanced example showing anti-detection, rate limiting, and structured extraction."""
2
2
 
3
3
  import asyncio
4
+
4
5
  from scrapeflow import ScrapeFlow
5
6
  from scrapeflow.config import (
6
- ScrapeFlowConfig,
7
7
  AntiDetectionConfig,
8
8
  RateLimitConfig,
9
9
  RetryConfig,
10
+ ScrapeFlowConfig,
10
11
  )
11
- from scrapeflow.extractors import StructuredExtractor, Extractor
12
+ from scrapeflow.extractors import Extractor, StructuredExtractor
12
13
 
13
14
 
14
15
  async def main():
@@ -65,8 +66,7 @@ async def main():
65
66
  # Extract tags separately using Extractor for each quote
66
67
  quotes_with_tags = []
67
68
  quote_elements = scraper.page.locator(".quote")
68
- count = await quote_elements.count()
69
-
69
+
70
70
  for i, quote_data in enumerate(data.get("quotes", [])[:5]): # First 5
71
71
  quote_elem = quote_elements.nth(i)
72
72
  tags = await Extractor.extract_texts(quote_elem, ".tag")
@@ -84,7 +84,7 @@ async def main():
84
84
 
85
85
  # Get metrics
86
86
  metrics = scraper.get_metrics()
87
- print(f"\nScraping completed:")
87
+ print("\nScraping completed:")
88
88
  print(f" Success rate: {metrics.get_success_rate():.2f}%")
89
89
  print(f" Total requests: {metrics.total_requests}")
90
90
  print(f" Retries: {metrics.retry_count}")
@@ -92,4 +92,3 @@ async def main():
92
92
 
93
93
  if __name__ == "__main__":
94
94
  asyncio.run(main())
95
-
@@ -8,6 +8,7 @@ Demonstrates:
8
8
  """
9
9
 
10
10
  import asyncio
11
+
11
12
  from pydantic import BaseModel
12
13
 
13
14
  from scrapeflow import (
@@ -16,7 +17,7 @@ from scrapeflow import (
16
17
  get_registry,
17
18
  register_quotes_login_handler,
18
19
  )
19
- from scrapeflow.config import ScrapeFlowConfig, EthicalCrawlingConfig
20
+ from scrapeflow.config import EthicalCrawlingConfig, ScrapeFlowConfig
20
21
  from scrapeflow.specifications import FieldSpec, ItemSpec
21
22
 
22
23
 
@@ -5,8 +5,9 @@ a real website designed for scraping practice.
5
5
  """
6
6
 
7
7
  import asyncio
8
+
8
9
  from scrapeflow import ScrapeFlow
9
- from scrapeflow.config import ScrapeFlowConfig, RateLimitConfig, RetryConfig
10
+ from scrapeflow.config import RateLimitConfig, RetryConfig, ScrapeFlowConfig
10
11
  from scrapeflow.extractors import Extractor
11
12
 
12
13
 
@@ -11,17 +11,18 @@ Requires MISTRAL_API_KEY in .env for LLM fallback.
11
11
 
12
12
  import asyncio
13
13
  import os
14
- from typing import Optional
15
14
 
16
15
  try:
17
16
  from dotenv import load_dotenv
17
+
18
18
  load_dotenv()
19
19
  except ImportError:
20
20
  pass
21
21
 
22
22
  from pydantic import BaseModel, Field
23
- from scrapeflow import ScrapeFlow, HybridExtractor, SpecificationExtractor
24
- from scrapeflow.config import ScrapeFlowConfig, EthicalCrawlingConfig
23
+
24
+ from scrapeflow import HybridExtractor, ScrapeFlow
25
+ from scrapeflow.config import EthicalCrawlingConfig, ScrapeFlowConfig
25
26
  from scrapeflow.specifications import FieldSpec
26
27
 
27
28
 
@@ -30,7 +31,7 @@ class ProductSpec(BaseModel):
30
31
 
31
32
  title: str = Field(..., description="Product title")
32
33
  price: str = Field(..., description="Price string")
33
- availability: Optional[str] = Field(None, description="Stock status")
34
+ availability: str | None = Field(None, description="Stock status")
34
35
 
35
36
 
36
37
  async def run_hybrid_example():
@@ -47,7 +48,9 @@ async def run_hybrid_example():
47
48
  }
48
49
 
49
50
  async with ScrapeFlow(config) as scraper:
50
- await scraper.navigate("https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html")
51
+ await scraper.navigate(
52
+ "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"
53
+ )
51
54
 
52
55
  # Use HybridExtractor: selectors first, LLM fallback if validation fails
53
56
  use_llm = bool(os.environ.get("MISTRAL_API_KEY"))
@@ -15,13 +15,14 @@ import os
15
15
  # Load .env if available
16
16
  try:
17
17
  from dotenv import load_dotenv
18
+
18
19
  load_dotenv()
19
20
  except ImportError:
20
21
  pass
21
22
 
22
23
  from scrapeflow import ScrapeFlow, create_mcp_backend
23
- from scrapeflow.config import ScrapeFlowConfig, EthicalCrawlingConfig
24
- from scrapeflow.llm_extract import generate_schema_from_prompt, extract_with_schema
24
+ from scrapeflow.config import EthicalCrawlingConfig, ScrapeFlowConfig
25
+ from scrapeflow.llm_extract import extract_with_schema, generate_schema_from_prompt
25
26
 
26
27
 
27
28
  async def run_llm_extraction():
@@ -38,15 +39,11 @@ async def run_llm_extraction():
38
39
  await scraper.navigate("https://quotes.toscrape.com/")
39
40
 
40
41
  # Option 1: Generate schema from prompt
41
- schema = generate_schema_from_prompt(
42
- "Extract quote text, author name, and list of tags"
43
- )
42
+ schema = generate_schema_from_prompt("Extract quote text, author name, and list of tags")
44
43
  print("Generated schema:", schema)
45
44
 
46
45
  # Option 2: Extract using schema + page content
47
- content = await scraper.page.evaluate(
48
- "() => document.body.innerText"
49
- )
46
+ content = await scraper.page.evaluate("() => document.body.innerText")
50
47
  data = extract_with_schema(
51
48
  content,
52
49
  schema,
@@ -10,11 +10,17 @@ import asyncio
10
10
  import os
11
11
  import tempfile
12
12
 
13
- from scrapeflow import ScrapeFlow, SpecificationExtractor, get_registry, register_quotes_login_handler
14
- from scrapeflow.config import ScrapeFlowConfig, EthicalCrawlingConfig, BrowserConfig
15
- from scrapeflow.specifications import FieldSpec, ItemSpec
16
13
  from pydantic import BaseModel
17
14
 
15
+ from scrapeflow import (
16
+ ScrapeFlow,
17
+ SpecificationExtractor,
18
+ get_registry,
19
+ register_quotes_login_handler,
20
+ )
21
+ from scrapeflow.config import BrowserConfig, EthicalCrawlingConfig, ScrapeFlowConfig
22
+ from scrapeflow.specifications import FieldSpec, ItemSpec
23
+
18
24
 
19
25
  class QuoteItem(BaseModel):
20
26
  text: str
@@ -10,16 +10,16 @@ Demonstrates:
10
10
  """
11
11
 
12
12
  import asyncio
13
- from pydantic import BaseModel, Field
13
+
14
+ from pydantic import BaseModel
14
15
 
15
16
  from scrapeflow import ScrapeFlow, SpecificationExtractor
16
17
  from scrapeflow.config import (
17
- ScrapeFlowConfig,
18
18
  EthicalCrawlingConfig,
19
19
  RateLimitConfig,
20
+ ScrapeFlowConfig,
20
21
  )
21
22
  from scrapeflow.specifications import FieldSpec, ItemSpec, JobListingSpec
22
- from scrapeflow.registry import get_registry, register_job_listing_schema
23
23
 
24
24
 
25
25
  class JobListingsPage(BaseModel):
@@ -73,7 +73,9 @@ async def scrape_job_listings():
73
73
  print(f" {i+1}. {job.title[:50]}... | {job.company or 'N/A'}")
74
74
 
75
75
  metrics = scraper.get_metrics()
76
- print(f"\nMetrics: {metrics.total_requests} requests, {metrics.get_success_rate():.1f}% success")
76
+ print(
77
+ f"\nMetrics: {metrics.total_requests} requests, {metrics.get_success_rate():.1f}% success"
78
+ )
77
79
 
78
80
 
79
81
  if __name__ == "__main__":
@@ -5,6 +5,7 @@ a real e-commerce site designed for scraping practice.
5
5
  """
6
6
 
7
7
  import asyncio
8
+
8
9
  from scrapeflow import ScrapeFlow, Workflow
9
10
  from scrapeflow.config import ScrapeFlowConfig
10
11
  from scrapeflow.extractors import StructuredExtractor
@@ -20,12 +21,12 @@ async def scrape_books_page(page, context):
20
21
  "title": "h3 a",
21
22
  "price": ".price_color",
22
23
  "availability": ".instock.availability",
23
- }
24
+ },
24
25
  }
25
26
  }
26
27
  extractor = StructuredExtractor(schema)
27
28
  data = await extractor.extract(page)
28
-
29
+
29
30
  context["books_data"] = data
30
31
  return data
31
32
 
@@ -90,7 +91,7 @@ async def main():
90
91
 
91
92
  # Print metrics
92
93
  metrics = scraper.get_metrics()
93
- print(f"\n📊 Metrics:")
94
+ print("\n📊 Metrics:")
94
95
  print(f" Total requests: {metrics.total_requests}")
95
96
  print(f" Success rate: {metrics.get_success_rate():.2f}%")
96
97
  print(f" Average response time: {metrics.average_response_time:.2f}s")
@@ -98,4 +99,3 @@ async def main():
98
99
 
99
100
  if __name__ == "__main__":
100
101
  asyncio.run(main())
101
-