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.
- scrapper_tool-0.1.0/.gitignore +44 -0
- scrapper_tool-0.1.0/CHANGELOG.md +46 -0
- scrapper_tool-0.1.0/LICENSE +21 -0
- scrapper_tool-0.1.0/PKG-INFO +235 -0
- scrapper_tool-0.1.0/README.md +192 -0
- scrapper_tool-0.1.0/docs/agent-integration/.gitkeep +1 -0
- scrapper_tool-0.1.0/docs/agent-integration.md +33 -0
- scrapper_tool-0.1.0/docs/index.md +22 -0
- scrapper_tool-0.1.0/docs/patterns/a-json-api.md +9 -0
- scrapper_tool-0.1.0/docs/patterns/b-embedded-json.md +17 -0
- scrapper_tool-0.1.0/docs/patterns/c-css-microdata.md +11 -0
- scrapper_tool-0.1.0/docs/patterns/d-hostile.md +15 -0
- scrapper_tool-0.1.0/docs/quickstart.md +14 -0
- scrapper_tool-0.1.0/docs/recon.md +14 -0
- scrapper_tool-0.1.0/docs/reference/http.md +11 -0
- scrapper_tool-0.1.0/docs/reference/ladder.md +20 -0
- scrapper_tool-0.1.0/docs/reference/testing.md +12 -0
- scrapper_tool-0.1.0/docs/research/2026-04-30-landscape.md +197 -0
- scrapper_tool-0.1.0/docs/research/do-not-adopt.md +35 -0
- scrapper_tool-0.1.0/docs/research/tool-catalog.md +16 -0
- scrapper_tool-0.1.0/pyproject.toml +137 -0
- scrapper_tool-0.1.0/src/scrapper_tool/__init__.py +66 -0
- scrapper_tool-0.1.0/src/scrapper_tool/_logging.py +78 -0
- scrapper_tool-0.1.0/src/scrapper_tool/adapter.py +127 -0
- scrapper_tool-0.1.0/src/scrapper_tool/canary.py +297 -0
- scrapper_tool-0.1.0/src/scrapper_tool/errors.py +68 -0
- scrapper_tool-0.1.0/src/scrapper_tool/http.py +288 -0
- scrapper_tool-0.1.0/src/scrapper_tool/ladder.py +209 -0
- scrapper_tool-0.1.0/src/scrapper_tool/patterns/__init__.py +15 -0
- scrapper_tool-0.1.0/src/scrapper_tool/patterns/b.py +262 -0
- scrapper_tool-0.1.0/src/scrapper_tool/patterns/c.py +210 -0
- scrapper_tool-0.1.0/src/scrapper_tool/patterns/d.py +166 -0
- scrapper_tool-0.1.0/src/scrapper_tool/testing.py +215 -0
- scrapper_tool-0.1.0/tests/conftest.py +20 -0
- scrapper_tool-0.1.0/tests/integration/__init__.py +0 -0
- scrapper_tool-0.1.0/tests/unit/__init__.py +0 -0
- scrapper_tool-0.1.0/tests/unit/test_adapter_protocol.py +100 -0
- scrapper_tool-0.1.0/tests/unit/test_canary.py +190 -0
- scrapper_tool-0.1.0/tests/unit/test_errors.py +41 -0
- scrapper_tool-0.1.0/tests/unit/test_http.py +198 -0
- scrapper_tool-0.1.0/tests/unit/test_ladder.py +177 -0
- scrapper_tool-0.1.0/tests/unit/test_patterns_b.py +237 -0
- scrapper_tool-0.1.0/tests/unit/test_patterns_c.py +192 -0
- scrapper_tool-0.1.0/tests/unit/test_patterns_d.py +150 -0
- scrapper_tool-0.1.0/tests/unit/test_smoke.py +35 -0
- 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
|
+
[](https://github.com/ValeroK/scrapper-tool/actions/workflows/ci.yml)
|
|
55
|
+
[](https://pypi.org/project/scrapper-tool/)
|
|
56
|
+
[](https://pypi.org/project/scrapper-tool/)
|
|
57
|
+
[](https://pypi.org/project/scrapper-tool/)
|
|
58
|
+
[](LICENSE)
|
|
59
|
+
[](https://github.com/astral-sh/ruff)
|
|
60
|
+
[](https://mypy-lang.org/)
|
|
61
|
+
[](CONTRIBUTING.md)
|
|
62
|
+
[](https://github.com/ValeroK/scrapper-tool/stargazers)
|
|
63
|
+
[](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
|
+
[](https://github.com/ValeroK/scrapper-tool/actions/workflows/ci.yml)
|
|
12
|
+
[](https://pypi.org/project/scrapper-tool/)
|
|
13
|
+
[](https://pypi.org/project/scrapper-tool/)
|
|
14
|
+
[](https://pypi.org/project/scrapper-tool/)
|
|
15
|
+
[](LICENSE)
|
|
16
|
+
[](https://github.com/astral-sh/ruff)
|
|
17
|
+
[](https://mypy-lang.org/)
|
|
18
|
+
[](CONTRIBUTING.md)
|
|
19
|
+
[](https://github.com/ValeroK/scrapper-tool/stargazers)
|
|
20
|
+
[](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).
|