scrapper-tool 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 (46) hide show
  1. scrapper_tool-0.1.0/.gitignore +44 -0
  2. scrapper_tool-0.1.0/CHANGELOG.md +46 -0
  3. scrapper_tool-0.1.0/LICENSE +21 -0
  4. scrapper_tool-0.1.0/PKG-INFO +235 -0
  5. scrapper_tool-0.1.0/README.md +192 -0
  6. scrapper_tool-0.1.0/docs/agent-integration/.gitkeep +1 -0
  7. scrapper_tool-0.1.0/docs/agent-integration.md +33 -0
  8. scrapper_tool-0.1.0/docs/index.md +22 -0
  9. scrapper_tool-0.1.0/docs/patterns/a-json-api.md +9 -0
  10. scrapper_tool-0.1.0/docs/patterns/b-embedded-json.md +17 -0
  11. scrapper_tool-0.1.0/docs/patterns/c-css-microdata.md +11 -0
  12. scrapper_tool-0.1.0/docs/patterns/d-hostile.md +15 -0
  13. scrapper_tool-0.1.0/docs/quickstart.md +14 -0
  14. scrapper_tool-0.1.0/docs/recon.md +14 -0
  15. scrapper_tool-0.1.0/docs/reference/http.md +11 -0
  16. scrapper_tool-0.1.0/docs/reference/ladder.md +20 -0
  17. scrapper_tool-0.1.0/docs/reference/testing.md +12 -0
  18. scrapper_tool-0.1.0/docs/research/2026-04-30-landscape.md +197 -0
  19. scrapper_tool-0.1.0/docs/research/do-not-adopt.md +35 -0
  20. scrapper_tool-0.1.0/docs/research/tool-catalog.md +16 -0
  21. scrapper_tool-0.1.0/pyproject.toml +137 -0
  22. scrapper_tool-0.1.0/src/scrapper_tool/__init__.py +66 -0
  23. scrapper_tool-0.1.0/src/scrapper_tool/_logging.py +78 -0
  24. scrapper_tool-0.1.0/src/scrapper_tool/adapter.py +127 -0
  25. scrapper_tool-0.1.0/src/scrapper_tool/canary.py +297 -0
  26. scrapper_tool-0.1.0/src/scrapper_tool/errors.py +68 -0
  27. scrapper_tool-0.1.0/src/scrapper_tool/http.py +288 -0
  28. scrapper_tool-0.1.0/src/scrapper_tool/ladder.py +209 -0
  29. scrapper_tool-0.1.0/src/scrapper_tool/patterns/__init__.py +15 -0
  30. scrapper_tool-0.1.0/src/scrapper_tool/patterns/b.py +262 -0
  31. scrapper_tool-0.1.0/src/scrapper_tool/patterns/c.py +210 -0
  32. scrapper_tool-0.1.0/src/scrapper_tool/patterns/d.py +166 -0
  33. scrapper_tool-0.1.0/src/scrapper_tool/testing.py +215 -0
  34. scrapper_tool-0.1.0/tests/conftest.py +20 -0
  35. scrapper_tool-0.1.0/tests/integration/__init__.py +0 -0
  36. scrapper_tool-0.1.0/tests/unit/__init__.py +0 -0
  37. scrapper_tool-0.1.0/tests/unit/test_adapter_protocol.py +100 -0
  38. scrapper_tool-0.1.0/tests/unit/test_canary.py +190 -0
  39. scrapper_tool-0.1.0/tests/unit/test_errors.py +41 -0
  40. scrapper_tool-0.1.0/tests/unit/test_http.py +198 -0
  41. scrapper_tool-0.1.0/tests/unit/test_ladder.py +177 -0
  42. scrapper_tool-0.1.0/tests/unit/test_patterns_b.py +237 -0
  43. scrapper_tool-0.1.0/tests/unit/test_patterns_c.py +192 -0
  44. scrapper_tool-0.1.0/tests/unit/test_patterns_d.py +150 -0
  45. scrapper_tool-0.1.0/tests/unit/test_smoke.py +35 -0
  46. scrapper_tool-0.1.0/tests/unit/test_testing_helpers.py +128 -0
@@ -0,0 +1,44 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ .Python
8
+ build/
9
+ dist/
10
+ *.egg-info/
11
+ *.egg
12
+ .eggs/
13
+
14
+ # Unit test / coverage reports
15
+ .pytest_cache/
16
+ .coverage
17
+ .coverage.*
18
+ htmlcov/
19
+ coverage.xml
20
+ .mypy_cache/
21
+ .ruff_cache/
22
+
23
+ # Environments
24
+ .env
25
+ .venv
26
+ venv/
27
+ ENV/
28
+
29
+ # IDEs
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+
35
+ # uv
36
+ .python-version
37
+
38
+ # Live-probe artifacts (gitignored — kept local for diagnostics)
39
+ reports/
40
+ docs/_evidence/
41
+
42
+ # OS
43
+ .DS_Store
44
+ Thumbs.db
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ All notable changes to `scrapper-tool` are recorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [SemVer](https://semver.org/).
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.0] - 2026-04-30
8
+
9
+ First public release. Covers Pattern A/B/C/D extraction primitives, the four-profile anti-bot impersonation ladder, deterministic fixture-replay testing, the generic `Adapter` Protocol, and a `scrapper-tool canary` CLI.
10
+
11
+ ### Added
12
+ - M0 — repo bootstrap: `pyproject.toml`, MIT `LICENSE`, README, governance files (`CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`), CI workflow (`.github/workflows/ci.yml` — ruff + mypy --strict + pytest + pip-audit on py3.12/3.13/3.14 matrix), tag-triggered PyPI release workflow (`.github/workflows/release.yml`, OIDC trusted-publisher).
13
+ - `[project.optional-dependencies]` placeholders for `hostile` (Scrapling) and `agent` (MCP — populated in M13).
14
+ - M1 — HTTP core extracted from PartsPilot's `affiliate-service`: `scrapper_tool.http.vendor_client()` (httpx + curl_cffi backends, async context manager) and `scrapper_tool.http.request_with_retry()` (3 attempts, exponential backoff with ±25% jitter, retries 429/5xx/transport errors, no-retry on 4xx ≠ 429, X-Request-ID injection).
15
+ - M1 — Exception hierarchy: `ScrapingError` (base), `VendorHTTPError`, `VendorUnavailable` (alias), `BlockedError`, `ParseError`. `BlockedError` and `ParseError` deliberately do NOT inherit from `VendorHTTPError` — circuit breakers should catch one but not the others.
16
+ - M1 — Optional `structlog` integration via `scrapper_tool._logging.get_logger()`; falls back to a stdlib `logging` adapter that accepts the same `key=value` kwarg shape.
17
+ - M1 — Top-level re-exports: `scrapper_tool.{vendor_client, request_with_retry, VendorHTTPError, BlockedError, ParseError, ScrapingError, VendorUnavailable, VendorHTTPClient}`.
18
+ - M2 — Anti-bot impersonation ladder (`scrapper_tool.ladder`): `IMPERSONATE_LADDER = ("chrome133a", "chrome124", "safari18_0", "firefox135")` and `request_with_ladder(method, url, ...)` walking it top-to-bottom on 403/503. First profile to return ≠403/503 wins; all-403 raises `BlockedError` with a "escalate to Pattern D" message. Each ladder step opens a fresh `curl_cffi.AsyncSession` (one-shot per profile, sessions pinned to a single fingerprint). Logs winning profile via the structured logger (`ladder.profile_won` / `ladder.profile_blocked`).
19
+ - M2 — Re-exported at top level: `scrapper_tool.{IMPERSONATE_LADDER, request_with_ladder}`.
20
+ - M2 — 9 ladder unit tests (happy path, 403→200 fallback, 503 rotate-like-403, safari wins when chrome burns, all-403 raises, custom ladder, empty ladder ValueError, default-ladder shape, header propagation). Uses an inline `_FakeCurlSession` lifted to `scrapper_tool.testing` in M6.
21
+ - M3 — Pattern B helper (`scrapper_tool.patterns.b`): `extract_product_offer(html, base_url=None)` returns a normalised `ProductOffer` Pydantic model from any of JSON-LD / microdata / RDFa Product blocks. Handles top-level Products, Products nested inside `@graph`, multi-offer lists (takes first), price/currency nested inside `priceSpecification`, brand-as-dict-or-string, image-as-list-or-dict, all `gtin{,8,12,13,14}` variants. Powered by `extruct.extract(..., uniform=True)` so one walker covers all three syntaxes.
22
+ - M3 — `ProductOffer` model fields: `name`, `sku`, `mpn` (often the OEM in automotive use cases), `gtin`, `brand`, `description`, `image`, `price` (Decimal), `currency` (ISO 4217), `availability` (raw schema.org URI), `url`. `model_config = {"extra": "ignore"}` so vendors adding fields don't break parsing.
23
+ - M3 — 10 Pattern B unit tests (JSON-LD top-level, JSON-LD inside @graph, offers as list, priceSpecification fallback, microdata, brand-as-string, no-Product-block returns None, plain HTML returns None, base_url propagation, extra-keys ignored).
24
+ - M4 — Pattern C helper (`scrapper_tool.patterns.c`): `extract_microdata_price(html) -> tuple[Decimal, str] | None` for sites that ship `<meta itemprop="price"> + <meta itemprop="priceCurrency">` schema.org microdata anchors (preferred — stable across CSS reshuffles); `extract_via_selectors(html, *, price_selector, currency_selector=None, default_currency=None)` for last-resort bespoke CSS selectors. Backed by `selectolax` (lexbor backend; 30-40× faster than BeautifulSoup at our fetch volumes).
25
+ - M4 — Internal `_coerce_decimal` strips common currency glyphs (`$`, `€`, `£`, `₪`, `¥`) and US/UK thousands-separator commas before parsing. European decimal-comma is NOT supported by default — vendor-specific normalisation is the consumer's job.
26
+ - M4 — 21 Pattern C unit tests (microdata via `<meta>` content attribute, microdata via text fallback, price-without-currency returns None, missing microdata returns None, selector with default_currency, selector with currency_selector, selector with `data-price` attribute preferred, missing element returns None, ValueError on no-currency-source, glyph stripping for 6 currency symbols, thousands-separator stripping, unparseable input returns None).
27
+ - M5 — Pattern D helper (`scrapper_tool.patterns.d.hostile_client`): async context manager wrapping Scrapling's `StealthyFetcher` for Cloudflare Turnstile / Akamai EVA / Distil-class hostile sites. Lazy-imports `scrapling` so consumers without the `[hostile]` extra installed see a useful `ImportError` with install hint rather than `ModuleNotFoundError` at import time. Forwards `headless`, `block_resources`, `timeout`, and arbitrary `extra_kwargs` to the fetcher; supports both async (`aclose`) and sync (`close`) lifecycle on exit.
28
+ - M5 — 5 Pattern D unit tests (`ImportError` raised when `[hostile]` not installed, fetcher yielded + closed on exit, `extra_kwargs` propagate, sync-close fallback for older Scrapling versions, module docstring readable without scrapling installed). Real Scrapling integration deferred to live-probe tests (`tests/integration/test_live_probes.py`, `live` marker, opt-in).
29
+ - M6 — Test helpers (`scrapper_tool.testing`): `FakeCurlSession` (drop-in mock for `curl_cffi.AsyncSession` because `respx` doesn't intercept it), `FakeResponse` (minimal duck-typed response), `replay_fixture(path, parser)` (load fixture file from disk and feed to a parser), `assert_pydantic_snapshot(obj, path, *, write_if_missing=True)` (golden-snapshot diff for Pydantic models with first-run seeding).
30
+ - M6 — Refactored `tests/unit/test_ladder.py` to use the canonical `FakeCurlSession` (M2's inline mock removed; replaced with the import).
31
+ - M6 — 12 meta-tests in `tests/unit/test_testing_helpers.py` covering FakeResponse construction, FakeCurlSession reset/configuration/calls-tracking, replay_fixture text loading, snapshot first-run-write / pass-on-match / fail-on-drift / write_if_missing=False semantics. 100% coverage on `testing.py`.
32
+ - M5.5 — Filled `docs/research/2026-04-30-landscape.md` (~250 lines, 19 numbered sources). Eight sections: TLS-impersonation libraries, browser-stealth tools, anti-bot platforms in 2026, LLM-assisted scraping, HTML parsing libraries, structured-data extraction, what's deliberately missing from the lib, and a refresh policy that makes successor landscape docs append-only history rather than edits-in-place.
33
+
34
+ - M7 — Generic `Adapter[QueryT, ResultT]` Protocol (`scrapper_tool.adapter`). Structural typing with `runtime_checkable` so `isinstance(obj, Adapter)` works without inheritance. Required surface: `vendor_id: str` attribute + `async search(query)` + `async fetch_detail(url)`. Doc-strings codify the error-bubbling contract (VendorHTTPError → breaker trips; BlockedError → escalate to Pattern D; ParseError → don't trip breaker, parser drift bug). Re-exported as `scrapper_tool.Adapter`.
35
+ - M7 — 6 Protocol tests: complete impl satisfies isinstance, missing method fails, missing field fails, search round-trip, fetch_detail round-trip, fetch_detail returns None for missing URL.
36
+ - M8 — `scrapper-tool canary` CLI (`scrapper_tool.canary` module + `[project.scripts]` entry). Walks the impersonation ladder against a target URL, reports which profile won (or all-blocked). Designed for cron / GitHub Actions to surface "chrome133a is starting to 403" before any consumer adapter notices. Flags: `--profiles chrome133a,chrome124,...` (custom ladder), `--timeout` (per-request), `--proxy`, `--json` (machine-readable output). Exit codes: 0 success, 1 all-blocked, 2 error. Public API: `run_canary()` (programmatic) + `probe_profile()` (single-profile probe).
37
+ - M8 — 12 canary unit tests covering happy-path (first profile wins, others skipped), 403 fallback (rotates), all-blocked (exit_code=1), empty ladder ValueError, custom ladder, text mode, JSON mode parseable, --profiles override, exit codes, --help, no-subcommand argparse error, malformed --profiles flag.
38
+
39
+ ### Fixed
40
+ - CI: `pip-audit --skip-editable` so the build doesn't try to look up `scrapper-tool` itself on PyPI before v0.1.0 ships.
41
+
42
+ ### Notes
43
+ - Initial milestone scope and decision log live in [`docs/research/2026-04-30-landscape.md`](docs/research/2026-04-30-landscape.md) (filled in during M5.5).
44
+
45
+ ### Notes
46
+ - Initial milestone scope and decision log live in [`docs/research/2026-04-30-landscape.md`](docs/research/2026-04-30-landscape.md) (filled in during M5.5).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ValeroK
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,235 @@
1
+ Metadata-Version: 2.4
2
+ Name: scrapper-tool
3
+ Version: 0.1.0
4
+ Summary: Reusable web-scraping toolkit — Pattern A/B/C/D ladder, TLS-impersonation fallback chain, deterministic fixture-replay testing, and an optional MCP server for LLM agents.
5
+ Project-URL: Homepage, https://github.com/ValeroK/scrapper-tool
6
+ Project-URL: Repository, https://github.com/ValeroK/scrapper-tool
7
+ Project-URL: Issues, https://github.com/ValeroK/scrapper-tool/issues
8
+ Project-URL: Changelog, https://github.com/ValeroK/scrapper-tool/blob/main/CHANGELOG.md
9
+ Author: ValeroK
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: anti-bot,curl-cffi,extruct,html-parsing,llm-agents,mcp,web-scraping
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Text Processing :: Markup :: HTML
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Requires-Dist: curl-cffi>=0.7
27
+ Requires-Dist: extruct>=0.18
28
+ Requires-Dist: httpx>=0.27
29
+ Requires-Dist: pydantic>=2.5
30
+ Requires-Dist: selectolax>=0.3.21
31
+ Provides-Extra: agent
32
+ Provides-Extra: dev
33
+ Requires-Dist: mypy>=1.13; extra == 'dev'
34
+ Requires-Dist: pip-audit>=2.7; extra == 'dev'
35
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
36
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
37
+ Requires-Dist: pytest>=8; extra == 'dev'
38
+ Requires-Dist: respx>=0.21; extra == 'dev'
39
+ Requires-Dist: ruff>=0.9; extra == 'dev'
40
+ Provides-Extra: hostile
41
+ Requires-Dist: scrapling>=0.3; extra == 'hostile'
42
+ Description-Content-Type: text/markdown
43
+
44
+ <div align="center">
45
+
46
+ # scrapper-tool
47
+
48
+ **A reusable Python web-scraping toolkit — production-grade primitives, anti-bot ladder, fixture-replay testing.**
49
+
50
+ Built from the scraping core behind [PartsPilot](https://github.com/ValeroK/affiliate-service), extracted as an open-source library so other projects (and LLM agents) can pick up the same patterns without redoing the reverse-engineering work.
51
+
52
+ <br />
53
+
54
+ [![CI](https://github.com/ValeroK/scrapper-tool/actions/workflows/ci.yml/badge.svg)](https://github.com/ValeroK/scrapper-tool/actions/workflows/ci.yml)
55
+ [![PyPI version](https://img.shields.io/pypi/v/scrapper-tool.svg)](https://pypi.org/project/scrapper-tool/)
56
+ [![Python versions](https://img.shields.io/pypi/pyversions/scrapper-tool.svg)](https://pypi.org/project/scrapper-tool/)
57
+ [![Downloads](https://img.shields.io/pypi/dm/scrapper-tool.svg)](https://pypi.org/project/scrapper-tool/)
58
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
59
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-261230.svg)](https://github.com/astral-sh/ruff)
60
+ [![Type-checked: mypy](https://img.shields.io/badge/type--checked-mypy-1f5082.svg)](https://mypy-lang.org/)
61
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
62
+ [![GitHub Stars](https://img.shields.io/github/stars/ValeroK/scrapper-tool?style=social)](https://github.com/ValeroK/scrapper-tool/stargazers)
63
+ [![GitHub Forks](https://img.shields.io/github/forks/ValeroK/scrapper-tool?style=social)](https://github.com/ValeroK/scrapper-tool/network/members)
64
+
65
+ [**Quickstart**](#quickstart) · [**Documentation**](docs/index.md) · [**Recon playbook**](docs/recon.md) · [**Changelog**](CHANGELOG.md) · [**Contributing**](CONTRIBUTING.md)
66
+
67
+ </div>
68
+
69
+ ---
70
+
71
+ > **Status (2026-04-30):** alpha. `v0.1.0` covers the core pattern ladder, anti-bot helpers, and deterministic fixture-replay testing. `v0.2.0` adds an MCP server for LLM agents (Claude, OpenClaw, Hermes Agent, AutoGen, LangChain).
72
+
73
+ ## Table of contents
74
+
75
+ - [Why scrapper-tool](#why-scrapper-tool)
76
+ - [The four scraping patterns](#the-four-scraping-patterns)
77
+ - [Architecture](#architecture)
78
+ - [Install](#install)
79
+ - [Quickstart](#quickstart)
80
+ - [Documentation](#documentation)
81
+ - [Why these tools?](#why-these-tools)
82
+ - [Roadmap](#roadmap)
83
+ - [Contributing](#contributing)
84
+ - [Contributors](#contributors)
85
+ - [Acknowledgements](#acknowledgements)
86
+ - [License](#license)
87
+
88
+ ## Why scrapper-tool
89
+
90
+ Most scrapers are written from scratch every time, even though 90% of the work is the same: pick the right extraction pattern, survive the TLS fingerprint, retry/backoff sanely, and write tests that don't drift the moment a site updates.
91
+
92
+ `scrapper-tool` packages the parts that don't change per vendor, so you only write the parts that do.
93
+
94
+ - **Pattern-first design.** Four named, documented extraction patterns (A–D) — pick the one DevTools points at, skip the rest.
95
+ - **Anti-bot ladder built in.** Auto-walks `chrome133a → chrome124 → safari18_0 → firefox135` when a profile gets fingerprinted.
96
+ - **Deterministic tests.** Fixture-replay (`FakeCurlSession`, `replay_fixture`, golden snapshots) — no live HTTP in CI.
97
+ - **Optional hostile mode.** Cloudflare Turnstile / Akamai EVA defeat path via [Scrapling](https://github.com/D4Vinci/Scrapling) — opt-in extra, no Playwright bloat by default.
98
+ - **LLM-agent ready.** `v0.2.0+` ships an MCP server so Claude, AutoGen, LangChain, etc. can drive the scraper directly.
99
+ - **Boring stack.** `httpx`, `curl_cffi`, `selectolax`, `extruct`. No managed SaaS bundled — your code, your egress.
100
+
101
+ ## The four scraping patterns
102
+
103
+ Web scraping in 2026 is dominated by four recurring patterns. This lib gives each pattern a documented helper plus the surrounding infrastructure (HTTP client with TLS-impersonation fallback, retry/backoff, fixture-replay testing) so you don't reinvent them per vendor.
104
+
105
+ | Pattern | When to use | Helper | Cost |
106
+ |---|---|---|---|
107
+ | **A — JSON API** | DevTools shows an XHR returning the price-bearing JSON. Anonymous or OAuth. | `vendor_client()` + your own response model | Lowest — parse, validate, done. |
108
+ | **B — Embedded JSON** | Document HTML carries `<script type="application/ld+json">`, `__NEXT_DATA__`, `__NUXT__`, or `self.__next_f.push(...)`. | `patterns.b.extract_product_offer()` (via [`extruct`](https://github.com/scrapinghub/extruct)) | Low — one call, broad markup coverage. |
109
+ | **C — CSS / microdata** | Price visible in HTML, no embedded JSON. Prefer `itemprop="price"` schema.org microdata. | `patterns.c.extract_microdata_price()` (via [`selectolax`](https://github.com/rushter/selectolax)) | Medium — selectors break on ancestor reshuffles. |
110
+ | **D — Hostile** | Cloudflare Turnstile, Akamai EVA, etc. defeat both default `httpx` and `curl_cffi`. | `patterns.d.hostile_client()` (via [Scrapling](https://github.com/D4Vinci/Scrapling)) — `pip install scrapper-tool[hostile]` | Highest — Playwright runtime, ≈400 MB image bloat. |
111
+
112
+ Plus a four-profile **anti-bot ladder** (`chrome133a → chrome124 → safari18_0 → firefox135`) that auto-walks when a profile gets fingerprinted, and a `scrapper-tool canary` CLI for nightly fingerprint-health probes.
113
+
114
+ ## Architecture
115
+
116
+ ```mermaid
117
+ flowchart TD
118
+ A[Your scraper code] --> B[vendor_client / request_with_retry]
119
+ B --> C{TLS-sensitive?}
120
+ C -- no --> D[httpx]
121
+ C -- yes --> E[curl_cffi ladder]
122
+ E --> E1[chrome133a] --> E2[chrome124] --> E3[safari18_0] --> E4[firefox135]
123
+ D --> F[Response]
124
+ E4 --> F
125
+ F --> G{Pattern}
126
+ G -- A --> H[JSON API model]
127
+ G -- B --> I[extruct: ld+json / next_data / nuxt]
128
+ G -- C --> J[selectolax: microdata / CSS]
129
+ G -- D --> K["Scrapling (Playwright + Turnstile)"]
130
+ H --> L[Validated product data]
131
+ I --> L
132
+ J --> L
133
+ K --> L
134
+ ```
135
+
136
+ ## Install
137
+
138
+ ```bash
139
+ pip install scrapper-tool # core: httpx + curl_cffi + selectolax + extruct
140
+ pip install scrapper-tool[hostile] # adds Scrapling for Cloudflare Turnstile
141
+ pip install scrapper-tool[agent] # adds the MCP server (v0.2.0+) for LLM agents
142
+ ```
143
+
144
+ > **Tip.** The `[hostile]` extra pulls Playwright (~400 MB). Don't install it unless you actually need pattern D.
145
+
146
+ ## Quickstart
147
+
148
+ ```python
149
+ import asyncio
150
+ from scrapper_tool import vendor_client, request_with_retry
151
+ from scrapper_tool.patterns.b import extract_product_offer
152
+
153
+ async def main() -> None:
154
+ async with vendor_client() as client:
155
+ resp = await request_with_retry(client, "GET", "https://example-shop.test/product/123")
156
+ product = extract_product_offer(resp.text, base_url=str(resp.url))
157
+ print(product)
158
+
159
+ asyncio.run(main())
160
+ ```
161
+
162
+ For TLS-sensitive vendors, flip one switch:
163
+
164
+ ```python
165
+ async with vendor_client(use_curl_cffi=True) as client:
166
+ ... # walks chrome133a → chrome124 → safari → firefox until one returns 200
167
+ ```
168
+
169
+ See **[`docs/quickstart.md`](docs/quickstart.md)** for a 5-minute on-ramp covering all four patterns.
170
+
171
+ ## Documentation
172
+
173
+ | | |
174
+ |---|---|
175
+ | **[Quickstart](docs/quickstart.md)** | 5-minute on-ramp. |
176
+ | **[Recon playbook](docs/recon.md)** | DevTools-driven reverse-engineering of a new vendor site. |
177
+ | **[Pattern A — JSON API](docs/patterns/a-json-api.md)** | Vendor exposes an XHR / JSON endpoint. |
178
+ | **[Pattern B — Embedded JSON](docs/patterns/b-embedded-json.md)** | `ld+json`, `__NEXT_DATA__`, `__NUXT__`, RSC payloads. |
179
+ | **[Pattern C — CSS / microdata](docs/patterns/c-css-microdata.md)** | `itemprop="price"`, fallback selectors. |
180
+ | **[Pattern D — Hostile](docs/patterns/d-hostile.md)** | Cloudflare Turnstile, Akamai EVA. |
181
+ | **[Anti-bot ladder reference](docs/reference/ladder.md)** | How the ladder walks, when to bump the primary profile. |
182
+ | **[Test helpers](docs/reference/testing.md)** | `FakeCurlSession`, `replay_fixture`, golden-snapshot pattern. |
183
+ | **[Agent integration](docs/agent-integration.md)** | MCP wiring for Claude, OpenClaw, Hermes Agent, AutoGen, LangChain. *(v0.2.0+)* |
184
+ | **[2026-04-30 landscape research](docs/research/2026-04-30-landscape.md)** | Why these tools, sourced. |
185
+
186
+ ## Why these tools?
187
+
188
+ Short version: `curl_cffi` is the only actively-maintained TLS-impersonation lib with `chrome131+`/`chrome133a`/`chrome142`/`chrome146` profiles; `puppeteer-stealth` and `playwright-extra` were deprecated in 2025-02; Scrapling is the only OSS Playwright-based stack with a working Turnstile auto-solve as of 2026; managed SaaS (Firecrawl, ZenRows, Bright Data) is deliberately not bundled.
189
+
190
+ Full sourced rationale: **[`docs/research/2026-04-30-landscape.md`](docs/research/2026-04-30-landscape.md)**.
191
+
192
+ ## Roadmap
193
+
194
+ - [x] **v0.1.0** — Core HTTP client, retry/backoff, anti-bot ladder, patterns A–D, fixture-replay test helpers.
195
+ - [ ] **v0.2.0** — MCP server for LLM agents; canary CLI for nightly fingerprint-health probes.
196
+ - [ ] **v0.3.0** — Pluggable rate-limit / robots.txt policies; per-vendor profile presets.
197
+ - [ ] **v1.0.0** — API stability guarantee; broader pattern-D backends.
198
+
199
+ See [`CHANGELOG.md`](CHANGELOG.md) for landed changes and [open issues](https://github.com/ValeroK/scrapper-tool/issues) for what's in flight.
200
+
201
+ ## Contributing
202
+
203
+ PRs and issues are welcome. Every PR that meaningfully changes how we scrape lands a `CHANGELOG.md` row.
204
+
205
+ - Read **[`CONTRIBUTING.md`](CONTRIBUTING.md)** for the maintenance contract.
206
+ - Read **[`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)** before opening a discussion.
207
+ - Good first issues live under the [`good first issue`](https://github.com/ValeroK/scrapper-tool/labels/good%20first%20issue) label.
208
+
209
+ ## Contributors
210
+
211
+ <a href="https://github.com/ValeroK/scrapper-tool/graphs/contributors">
212
+ <img src="https://contrib.rocks/image?repo=ValeroK/scrapper-tool" alt="Contributors" />
213
+ </a>
214
+
215
+ Want to see your avatar here? Check [CONTRIBUTING.md](CONTRIBUTING.md) and open a PR.
216
+
217
+ ## Acknowledgements
218
+
219
+ `scrapper-tool` stands on the shoulders of these projects:
220
+
221
+ - [`httpx`](https://github.com/encode/httpx) — async HTTP client
222
+ - [`curl_cffi`](https://github.com/lexiforest/curl_cffi) — TLS / JA3 impersonation
223
+ - [`selectolax`](https://github.com/rushter/selectolax) — fast HTML parsing
224
+ - [`extruct`](https://github.com/scrapinghub/extruct) — `ld+json`, microdata, RDFa extraction
225
+ - [`Scrapling`](https://github.com/D4Vinci/Scrapling) — Playwright-based hostile-site backend
226
+
227
+ ## License
228
+
229
+ [MIT](LICENSE) © scrapper-tool contributors.
230
+
231
+ <div align="center">
232
+
233
+ If `scrapper-tool` saves you time, consider [starring the repo](https://github.com/ValeroK/scrapper-tool) — it helps others find it.
234
+
235
+ </div>
@@ -0,0 +1,192 @@
1
+ <div align="center">
2
+
3
+ # scrapper-tool
4
+
5
+ **A reusable Python web-scraping toolkit — production-grade primitives, anti-bot ladder, fixture-replay testing.**
6
+
7
+ Built from the scraping core behind [PartsPilot](https://github.com/ValeroK/affiliate-service), extracted as an open-source library so other projects (and LLM agents) can pick up the same patterns without redoing the reverse-engineering work.
8
+
9
+ <br />
10
+
11
+ [![CI](https://github.com/ValeroK/scrapper-tool/actions/workflows/ci.yml/badge.svg)](https://github.com/ValeroK/scrapper-tool/actions/workflows/ci.yml)
12
+ [![PyPI version](https://img.shields.io/pypi/v/scrapper-tool.svg)](https://pypi.org/project/scrapper-tool/)
13
+ [![Python versions](https://img.shields.io/pypi/pyversions/scrapper-tool.svg)](https://pypi.org/project/scrapper-tool/)
14
+ [![Downloads](https://img.shields.io/pypi/dm/scrapper-tool.svg)](https://pypi.org/project/scrapper-tool/)
15
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
16
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-261230.svg)](https://github.com/astral-sh/ruff)
17
+ [![Type-checked: mypy](https://img.shields.io/badge/type--checked-mypy-1f5082.svg)](https://mypy-lang.org/)
18
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
19
+ [![GitHub Stars](https://img.shields.io/github/stars/ValeroK/scrapper-tool?style=social)](https://github.com/ValeroK/scrapper-tool/stargazers)
20
+ [![GitHub Forks](https://img.shields.io/github/forks/ValeroK/scrapper-tool?style=social)](https://github.com/ValeroK/scrapper-tool/network/members)
21
+
22
+ [**Quickstart**](#quickstart) · [**Documentation**](docs/index.md) · [**Recon playbook**](docs/recon.md) · [**Changelog**](CHANGELOG.md) · [**Contributing**](CONTRIBUTING.md)
23
+
24
+ </div>
25
+
26
+ ---
27
+
28
+ > **Status (2026-04-30):** alpha. `v0.1.0` covers the core pattern ladder, anti-bot helpers, and deterministic fixture-replay testing. `v0.2.0` adds an MCP server for LLM agents (Claude, OpenClaw, Hermes Agent, AutoGen, LangChain).
29
+
30
+ ## Table of contents
31
+
32
+ - [Why scrapper-tool](#why-scrapper-tool)
33
+ - [The four scraping patterns](#the-four-scraping-patterns)
34
+ - [Architecture](#architecture)
35
+ - [Install](#install)
36
+ - [Quickstart](#quickstart)
37
+ - [Documentation](#documentation)
38
+ - [Why these tools?](#why-these-tools)
39
+ - [Roadmap](#roadmap)
40
+ - [Contributing](#contributing)
41
+ - [Contributors](#contributors)
42
+ - [Acknowledgements](#acknowledgements)
43
+ - [License](#license)
44
+
45
+ ## Why scrapper-tool
46
+
47
+ Most scrapers are written from scratch every time, even though 90% of the work is the same: pick the right extraction pattern, survive the TLS fingerprint, retry/backoff sanely, and write tests that don't drift the moment a site updates.
48
+
49
+ `scrapper-tool` packages the parts that don't change per vendor, so you only write the parts that do.
50
+
51
+ - **Pattern-first design.** Four named, documented extraction patterns (A–D) — pick the one DevTools points at, skip the rest.
52
+ - **Anti-bot ladder built in.** Auto-walks `chrome133a → chrome124 → safari18_0 → firefox135` when a profile gets fingerprinted.
53
+ - **Deterministic tests.** Fixture-replay (`FakeCurlSession`, `replay_fixture`, golden snapshots) — no live HTTP in CI.
54
+ - **Optional hostile mode.** Cloudflare Turnstile / Akamai EVA defeat path via [Scrapling](https://github.com/D4Vinci/Scrapling) — opt-in extra, no Playwright bloat by default.
55
+ - **LLM-agent ready.** `v0.2.0+` ships an MCP server so Claude, AutoGen, LangChain, etc. can drive the scraper directly.
56
+ - **Boring stack.** `httpx`, `curl_cffi`, `selectolax`, `extruct`. No managed SaaS bundled — your code, your egress.
57
+
58
+ ## The four scraping patterns
59
+
60
+ Web scraping in 2026 is dominated by four recurring patterns. This lib gives each pattern a documented helper plus the surrounding infrastructure (HTTP client with TLS-impersonation fallback, retry/backoff, fixture-replay testing) so you don't reinvent them per vendor.
61
+
62
+ | Pattern | When to use | Helper | Cost |
63
+ |---|---|---|---|
64
+ | **A — JSON API** | DevTools shows an XHR returning the price-bearing JSON. Anonymous or OAuth. | `vendor_client()` + your own response model | Lowest — parse, validate, done. |
65
+ | **B — Embedded JSON** | Document HTML carries `<script type="application/ld+json">`, `__NEXT_DATA__`, `__NUXT__`, or `self.__next_f.push(...)`. | `patterns.b.extract_product_offer()` (via [`extruct`](https://github.com/scrapinghub/extruct)) | Low — one call, broad markup coverage. |
66
+ | **C — CSS / microdata** | Price visible in HTML, no embedded JSON. Prefer `itemprop="price"` schema.org microdata. | `patterns.c.extract_microdata_price()` (via [`selectolax`](https://github.com/rushter/selectolax)) | Medium — selectors break on ancestor reshuffles. |
67
+ | **D — Hostile** | Cloudflare Turnstile, Akamai EVA, etc. defeat both default `httpx` and `curl_cffi`. | `patterns.d.hostile_client()` (via [Scrapling](https://github.com/D4Vinci/Scrapling)) — `pip install scrapper-tool[hostile]` | Highest — Playwright runtime, ≈400 MB image bloat. |
68
+
69
+ Plus a four-profile **anti-bot ladder** (`chrome133a → chrome124 → safari18_0 → firefox135`) that auto-walks when a profile gets fingerprinted, and a `scrapper-tool canary` CLI for nightly fingerprint-health probes.
70
+
71
+ ## Architecture
72
+
73
+ ```mermaid
74
+ flowchart TD
75
+ A[Your scraper code] --> B[vendor_client / request_with_retry]
76
+ B --> C{TLS-sensitive?}
77
+ C -- no --> D[httpx]
78
+ C -- yes --> E[curl_cffi ladder]
79
+ E --> E1[chrome133a] --> E2[chrome124] --> E3[safari18_0] --> E4[firefox135]
80
+ D --> F[Response]
81
+ E4 --> F
82
+ F --> G{Pattern}
83
+ G -- A --> H[JSON API model]
84
+ G -- B --> I[extruct: ld+json / next_data / nuxt]
85
+ G -- C --> J[selectolax: microdata / CSS]
86
+ G -- D --> K["Scrapling (Playwright + Turnstile)"]
87
+ H --> L[Validated product data]
88
+ I --> L
89
+ J --> L
90
+ K --> L
91
+ ```
92
+
93
+ ## Install
94
+
95
+ ```bash
96
+ pip install scrapper-tool # core: httpx + curl_cffi + selectolax + extruct
97
+ pip install scrapper-tool[hostile] # adds Scrapling for Cloudflare Turnstile
98
+ pip install scrapper-tool[agent] # adds the MCP server (v0.2.0+) for LLM agents
99
+ ```
100
+
101
+ > **Tip.** The `[hostile]` extra pulls Playwright (~400 MB). Don't install it unless you actually need pattern D.
102
+
103
+ ## Quickstart
104
+
105
+ ```python
106
+ import asyncio
107
+ from scrapper_tool import vendor_client, request_with_retry
108
+ from scrapper_tool.patterns.b import extract_product_offer
109
+
110
+ async def main() -> None:
111
+ async with vendor_client() as client:
112
+ resp = await request_with_retry(client, "GET", "https://example-shop.test/product/123")
113
+ product = extract_product_offer(resp.text, base_url=str(resp.url))
114
+ print(product)
115
+
116
+ asyncio.run(main())
117
+ ```
118
+
119
+ For TLS-sensitive vendors, flip one switch:
120
+
121
+ ```python
122
+ async with vendor_client(use_curl_cffi=True) as client:
123
+ ... # walks chrome133a → chrome124 → safari → firefox until one returns 200
124
+ ```
125
+
126
+ See **[`docs/quickstart.md`](docs/quickstart.md)** for a 5-minute on-ramp covering all four patterns.
127
+
128
+ ## Documentation
129
+
130
+ | | |
131
+ |---|---|
132
+ | **[Quickstart](docs/quickstart.md)** | 5-minute on-ramp. |
133
+ | **[Recon playbook](docs/recon.md)** | DevTools-driven reverse-engineering of a new vendor site. |
134
+ | **[Pattern A — JSON API](docs/patterns/a-json-api.md)** | Vendor exposes an XHR / JSON endpoint. |
135
+ | **[Pattern B — Embedded JSON](docs/patterns/b-embedded-json.md)** | `ld+json`, `__NEXT_DATA__`, `__NUXT__`, RSC payloads. |
136
+ | **[Pattern C — CSS / microdata](docs/patterns/c-css-microdata.md)** | `itemprop="price"`, fallback selectors. |
137
+ | **[Pattern D — Hostile](docs/patterns/d-hostile.md)** | Cloudflare Turnstile, Akamai EVA. |
138
+ | **[Anti-bot ladder reference](docs/reference/ladder.md)** | How the ladder walks, when to bump the primary profile. |
139
+ | **[Test helpers](docs/reference/testing.md)** | `FakeCurlSession`, `replay_fixture`, golden-snapshot pattern. |
140
+ | **[Agent integration](docs/agent-integration.md)** | MCP wiring for Claude, OpenClaw, Hermes Agent, AutoGen, LangChain. *(v0.2.0+)* |
141
+ | **[2026-04-30 landscape research](docs/research/2026-04-30-landscape.md)** | Why these tools, sourced. |
142
+
143
+ ## Why these tools?
144
+
145
+ Short version: `curl_cffi` is the only actively-maintained TLS-impersonation lib with `chrome131+`/`chrome133a`/`chrome142`/`chrome146` profiles; `puppeteer-stealth` and `playwright-extra` were deprecated in 2025-02; Scrapling is the only OSS Playwright-based stack with a working Turnstile auto-solve as of 2026; managed SaaS (Firecrawl, ZenRows, Bright Data) is deliberately not bundled.
146
+
147
+ Full sourced rationale: **[`docs/research/2026-04-30-landscape.md`](docs/research/2026-04-30-landscape.md)**.
148
+
149
+ ## Roadmap
150
+
151
+ - [x] **v0.1.0** — Core HTTP client, retry/backoff, anti-bot ladder, patterns A–D, fixture-replay test helpers.
152
+ - [ ] **v0.2.0** — MCP server for LLM agents; canary CLI for nightly fingerprint-health probes.
153
+ - [ ] **v0.3.0** — Pluggable rate-limit / robots.txt policies; per-vendor profile presets.
154
+ - [ ] **v1.0.0** — API stability guarantee; broader pattern-D backends.
155
+
156
+ See [`CHANGELOG.md`](CHANGELOG.md) for landed changes and [open issues](https://github.com/ValeroK/scrapper-tool/issues) for what's in flight.
157
+
158
+ ## Contributing
159
+
160
+ PRs and issues are welcome. Every PR that meaningfully changes how we scrape lands a `CHANGELOG.md` row.
161
+
162
+ - Read **[`CONTRIBUTING.md`](CONTRIBUTING.md)** for the maintenance contract.
163
+ - Read **[`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)** before opening a discussion.
164
+ - Good first issues live under the [`good first issue`](https://github.com/ValeroK/scrapper-tool/labels/good%20first%20issue) label.
165
+
166
+ ## Contributors
167
+
168
+ <a href="https://github.com/ValeroK/scrapper-tool/graphs/contributors">
169
+ <img src="https://contrib.rocks/image?repo=ValeroK/scrapper-tool" alt="Contributors" />
170
+ </a>
171
+
172
+ Want to see your avatar here? Check [CONTRIBUTING.md](CONTRIBUTING.md) and open a PR.
173
+
174
+ ## Acknowledgements
175
+
176
+ `scrapper-tool` stands on the shoulders of these projects:
177
+
178
+ - [`httpx`](https://github.com/encode/httpx) — async HTTP client
179
+ - [`curl_cffi`](https://github.com/lexiforest/curl_cffi) — TLS / JA3 impersonation
180
+ - [`selectolax`](https://github.com/rushter/selectolax) — fast HTML parsing
181
+ - [`extruct`](https://github.com/scrapinghub/extruct) — `ld+json`, microdata, RDFa extraction
182
+ - [`Scrapling`](https://github.com/D4Vinci/Scrapling) — Playwright-based hostile-site backend
183
+
184
+ ## License
185
+
186
+ [MIT](LICENSE) © scrapper-tool contributors.
187
+
188
+ <div align="center">
189
+
190
+ If `scrapper-tool` saves you time, consider [starring the repo](https://github.com/ValeroK/scrapper-tool) — it helps others find it.
191
+
192
+ </div>
@@ -0,0 +1 @@
1
+ # Agent integration manifests + examples (populated in M13).
@@ -0,0 +1,33 @@
1
+ # Agent integration
2
+
3
+ > **Status: stub for v0.0.x. The MCP server lands in v0.2.0 (M13).** This doc is reserved at the top-level docs IA so the slot is visible from M0 — no retrofitting required when M13 ships.
4
+
5
+ ## What this will cover
6
+
7
+ `scrapper-tool` ships an optional MCP server (`pip install scrapper-tool[agent]`) that exposes the lib's helpers as tools any [Model Context Protocol](https://modelcontextprotocol.io)-compatible agent can call:
8
+
9
+ - `fetch_with_ladder` — issue an HTTP request through the four-profile impersonation ladder; report which profile won.
10
+ - `extract_product` — parse a schema.org Product+Offer block from HTML (Pattern B).
11
+ - `extract_microdata_price` — parse `itemprop="price"` schema.org microdata from HTML (Pattern C).
12
+ - `recon_classify` — classify a URL into Pattern A/B/C/D programmatically.
13
+ - `hostile_fetch` — Scrapling-backed fetch with auto-Turnstile-solve (requires `[hostile]` extra).
14
+ - `canary` — fire one probe per impersonation profile against a URL, report 200/403/timeout per profile.
15
+
16
+ Sections planned for M13:
17
+
18
+ 1. **MCP via Claude Code / Claude Desktop** — `.mcp.json` snippet.
19
+ 2. **MCP via the Anthropic SDK + `mcp-use`** — Python snippet.
20
+ 3. **OpenClaw integration** — manifest at `docs/agent-integration/openclaw.json`.
21
+ 4. **Hermes Agent integration** — manifest at `docs/agent-integration/hermes.yaml`.
22
+ 5. **AutoGen / LangChain via `mcp-use`** — code snippets.
23
+ 6. **Security note** — the MCP server runs in the user's trust boundary; tool docstrings call out unsafe inputs (raw URLs from untrusted content); the consuming agent's own permission model gates user-data-bearing fetches.
24
+
25
+ ## Why MCP
26
+
27
+ The 2026 agent stacks (Hermes Agent's plugin system, OpenClaw's tool registry, Composio connectors) all consume MCP servers. Reference implementations — Microsoft's [Playwright MCP](https://playwright.dev/python/agents) (~29k stars), Browserbase's Stagehand MCP (~21k stars), MaitreyaM's Crawl4AI MCP, luminati-io's web-scraping-with-mcp — all converge on the same shape.
28
+
29
+ The official Python MCP SDK (`mcp` package) is the canonical path; `mcp-use` bridges it to LangChain/AutoGen/the Anthropic SDK directly.
30
+
31
+ ---
32
+
33
+ *Detailed integration docs land in v0.2.0 (M13). Track [#TBD](https://github.com/ValeroK/scrapper-tool/issues) for the milestone.*
@@ -0,0 +1,22 @@
1
+ # scrapper-tool documentation
2
+
3
+ A reusable Python web-scraping toolkit. See the [README](../README.md) for the elevator pitch.
4
+
5
+ ## Table of contents
6
+
7
+ - **[Quickstart](quickstart.md)** — 5-minute on-ramp.
8
+ - **[Recon playbook](recon.md)** — DevTools-driven reverse-engineering of a new vendor site (the methodology behind PartsPilot's `scraping-vendor-recon` skill, generalised).
9
+ - **Pattern guides**:
10
+ - [Pattern A — JSON API](patterns/a-json-api.md)
11
+ - [Pattern B — Embedded JSON](patterns/b-embedded-json.md)
12
+ - [Pattern C — CSS / microdata](patterns/c-css-microdata.md)
13
+ - [Pattern D — Hostile (Cloudflare Turnstile, Akamai EVA, …)](patterns/d-hostile.md)
14
+ - **Reference**:
15
+ - [HTTP client](reference/http.md)
16
+ - [Anti-bot ladder](reference/ladder.md)
17
+ - [Test helpers](reference/testing.md)
18
+ - **Research**:
19
+ - [2026-04-30 landscape snapshot](research/2026-04-30-landscape.md) — why these tools, sourced.
20
+ - [Tool catalog](research/tool-catalog.md) — adopted / candidate / rejected matrix.
21
+ - [Do-not-adopt list](research/do-not-adopt.md) — append-only rejects with dates + reasons.
22
+ - **[Agent integration](agent-integration.md)** — MCP wiring for LLM agents (v0.2.0+).
@@ -0,0 +1,9 @@
1
+ # Pattern A — JSON API
2
+
3
+ > *Stub — populated in M9.*
4
+
5
+ **Signals**: DevTools Network → Fetch/XHR shows a request returning JSON with the price/availability fields you need. Endpoint may be anonymous, OAuth-gated, or behind a session cookie. Anonymous + stable is best.
6
+
7
+ **Helper**: there isn't one — Pattern A is just `vendor_client()` + `request_with_retry()` + your own Pydantic response model. The lib's job is to provide a polite HTTP client; the response shape is yours.
8
+
9
+ **Cost**: lowest. JSON parsing is faster + more robust than HTML scraping.
@@ -0,0 +1,17 @@
1
+ # Pattern B — Embedded JSON
2
+
3
+ > *Stub — populated in M3.*
4
+
5
+ **Signals**: the document response is HTML, but the price lives inside a `<script>` tag as JSON. Common markers:
6
+
7
+ | Marker | What it is |
8
+ |---|---|
9
+ | `<script type="application/ld+json">` with `"@type": "Product"` | schema.org Product/Offer block. **Most common modern pattern.** |
10
+ | `__NEXT_DATA__` | Next.js page data. |
11
+ | `window.__INITIAL_STATE__` | Older Vue/React SSR pattern. |
12
+ | `self.__next_f.push(...)` | Next.js 13+ App Router streaming. |
13
+ | `<script>window.__NUXT__ = ...</script>` | Nuxt SSR data. |
14
+
15
+ **Helper**: `scrapper_tool.patterns.b.extract_product_offer(html)` (via [extruct](https://github.com/scrapinghub/extruct)).
16
+
17
+ **Cost**: low. One call, broad markup coverage (handles RDFa, microdata, JSON-LD with multiple `@graph` nesting shapes).