webskrap 3.2.3__tar.gz → 3.2.4__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.
- {webskrap-3.2.3 → webskrap-3.2.4}/CHANGELOG.md +23 -1
- {webskrap-3.2.3 → webskrap-3.2.4}/CONTRIBUTING.md +1 -1
- {webskrap-3.2.3 → webskrap-3.2.4}/PKG-INFO +2 -1
- {webskrap-3.2.3 → webskrap-3.2.4}/SECURITY.md +4 -4
- {webskrap-3.2.3 → webskrap-3.2.4}/docs/diagnostics/datadome.md +3 -3
- {webskrap-3.2.3 → webskrap-3.2.4}/pyproject.toml +16 -3
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_cli.py +2 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/uv.lock +1 -1
- webskrap-3.2.3/.agents/skills/webskrap/SKILL.md +0 -151
- webskrap-3.2.3/.agents/skills/webskrap/references/cli.md +0 -154
- webskrap-3.2.3/.agents/skills/webskrap/references/mcp.md +0 -130
- webskrap-3.2.3/.agents/skills/webskrap/references/python-api.md +0 -147
- webskrap-3.2.3/.claude/skills/webskrap/SKILL.md +0 -1
- webskrap-3.2.3/.codex/skills/webskrap/SKILL.md +0 -1
- webskrap-3.2.3/tests/test_docs.py +0 -44
- {webskrap-3.2.3 → webskrap-3.2.4}/.gitattributes +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/.gitignore +0 -0
- {webskrap-3.2.3/.package-dist.zjESut → webskrap-3.2.4/.package-dist.sya6Sl}/.gitignore +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/LICENSE +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/README.md +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/benchmarks.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/docs/design/cdp-driver.md +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/docs/migration-3.0.md +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/scripts/driver_parity.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/scripts/live_stealth_report.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/__init__.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/browser_cli.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/browser_session.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/cdp.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/cdp_dom.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/cdp_session.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/cdp_setup.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/challenges.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/cli.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/cli_output.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/client.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/consent.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/diagnostics.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/display.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/errors.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/fetch_runtime.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/guide.md +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/human.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/install.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/mcp_server.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/models.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/parsing.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/paths.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/profiles.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/proxy_auth.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/py.typed +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/search.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/text_search.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/src/webskrap/urls.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/conftest.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/accepted-202.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/akamai-invalid-url-400.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/akamai-reference-403.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/akamai-sec-cpt.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/api-403.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/article-access-denied.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/cloudflare-1020.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/cloudflare-custom-block.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/blocks/not-found-404.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/search/bing.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/search/bing_challenge.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/search/bing_no_results.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/search/ddg.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/search/ddg_challenge.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/fixtures/search/ddg_no_results.html +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/live_stealth_helpers.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_bot_detection.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_bot_detection_headless.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_browser_cli.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_browser_integration.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_browser_session_unit.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_browser_vision.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_cdp.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_cdp_browser.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_cdp_child_targets.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_cdp_session.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_cdp_setup.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_challenges.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_client_unit.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_consent.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_consent_live.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_diagnostics.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_display.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_errors.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_fetch_runtime.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_human.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_install.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_live_stealth_helpers.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_live_stealth_report.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_mcp.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_mcp_efficiency.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_models.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_parsing.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_paths.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_profiles.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_search.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_search_live.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_text_search.py +0 -0
- {webskrap-3.2.3 → webskrap-3.2.4}/tests/test_urls.py +0 -0
|
@@ -9,6 +9,27 @@ history, so they summarize each release rather than list every change.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [3.2.4] - 2026-11-01
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Python 3.15 is a supported version. CI runs the test suite on it, and the
|
|
17
|
+
package metadata lists it.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- The source distribution no longer contains the agent skill folders and the
|
|
22
|
+
test that checks them. They stay in the Git repository. Before, the sdist
|
|
23
|
+
held only one of the linked skill reference paths, and its test failed.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- The CLI guide lists the exit status for each error code. It said before that
|
|
28
|
+
every browser command failure exits with 1.
|
|
29
|
+
- The MCP guide and the agent reference now describe `browser_wait_for`.
|
|
30
|
+
- `SECURITY.md` names the supported release line without an old version
|
|
31
|
+
number.
|
|
32
|
+
|
|
12
33
|
## [3.2.3] - 2026-10-30
|
|
13
34
|
|
|
14
35
|
### Security
|
|
@@ -980,7 +1001,8 @@ entries below.
|
|
|
980
1001
|
|
|
981
1002
|
Earlier releases (0.1.0 - 0.4.9) are recorded in the git history and tags only.
|
|
982
1003
|
|
|
983
|
-
[Unreleased]: https://github.com/kacigaya/webskrap/compare/v3.2.
|
|
1004
|
+
[Unreleased]: https://github.com/kacigaya/webskrap/compare/v3.2.4...HEAD
|
|
1005
|
+
[3.2.4]: https://github.com/kacigaya/webskrap/compare/v3.2.3...v3.2.4
|
|
984
1006
|
[3.2.3]: https://github.com/kacigaya/webskrap/compare/v3.2.2...v3.2.3
|
|
985
1007
|
[3.2.2]: https://github.com/kacigaya/webskrap/compare/v3.2.1...v3.2.2
|
|
986
1008
|
[3.2.1]: https://github.com/kacigaya/webskrap/compare/v3.2.0...v3.2.1
|
|
@@ -5,7 +5,7 @@ what a reviewable pull request looks like.
|
|
|
5
5
|
|
|
6
6
|
## Requirements
|
|
7
7
|
|
|
8
|
-
- Python 3.11, 3.12, 3.13, or 3.
|
|
8
|
+
- Python 3.11, 3.12, 3.13, 3.14, or 3.15 (CI runs all five).
|
|
9
9
|
- [uv](https://docs.astral.sh/uv/) for environments and dependencies.
|
|
10
10
|
- A Chromium build for browser tests, installed via `webskrap install`.
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: webskrap
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.4
|
|
4
4
|
Summary: Async Python scraping on Chromium over its own CDP client, with coherent browser profiles and session controls.
|
|
5
5
|
Project-URL: Homepage, https://github.com/kacigaya/webskrap
|
|
6
6
|
Project-URL: Repository, https://github.com/kacigaya/webskrap
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
21
|
Classifier: Typing :: Typed
|
|
21
22
|
Requires-Python: >=3.11
|
|
22
23
|
Requires-Dist: mcp<3,>=2
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
WebSkrap is developed on `main` and released from tags. Fixes land in a new
|
|
6
6
|
patch release on top of the latest minor; older minors are not backported.
|
|
7
7
|
|
|
8
|
-
| Version
|
|
9
|
-
|
|
|
10
|
-
| Latest minor
|
|
11
|
-
| Older minors
|
|
8
|
+
| Version | Supported |
|
|
9
|
+
| ------------ | --------- |
|
|
10
|
+
| Latest minor | Yes |
|
|
11
|
+
| Older minors | No |
|
|
12
12
|
|
|
13
13
|
## Reporting a vulnerability
|
|
14
14
|
|
|
@@ -129,9 +129,9 @@ was dated March 2024. Some listed endpoints timed out or rejected authentication
|
|
|
129
129
|
The catalog was refreshed through Gluetun's existing updater. The original
|
|
130
130
|
catalog was backed up, and the VPN was restored to a healthy state.
|
|
131
131
|
|
|
132
|
-
The final verified exit
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
The final verified exit still required verification. The refreshed catalog is
|
|
133
|
+
in the container's writable layer; container recreation can replace it because
|
|
134
|
+
there is no catalog volume mount.
|
|
135
135
|
|
|
136
136
|
## CDP identity investigation
|
|
137
137
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "webskrap"
|
|
7
|
-
version = "3.2.
|
|
7
|
+
version = "3.2.4"
|
|
8
8
|
description = "Async Python scraping on Chromium over its own CDP client, with coherent browser profiles and session controls."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -22,6 +22,7 @@ classifiers = [
|
|
|
22
22
|
"Programming Language :: Python :: 3.12",
|
|
23
23
|
"Programming Language :: Python :: 3.13",
|
|
24
24
|
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Programming Language :: Python :: 3.15",
|
|
25
26
|
"Typing :: Typed",
|
|
26
27
|
]
|
|
27
28
|
dependencies = [
|
|
@@ -56,8 +57,20 @@ packages = ["src/webskrap"]
|
|
|
56
57
|
|
|
57
58
|
[tool.hatch.build.targets.sdist]
|
|
58
59
|
# The web/ frontend ships only to GitHub Pages, never to PyPI. assets/ holds
|
|
59
|
-
# the README logo, which no install step reads.
|
|
60
|
-
|
|
60
|
+
# the README logo, which no install step reads. The agent skill folders link
|
|
61
|
+
# to one shared references/ copy, and Hatch keeps only one path to a linked
|
|
62
|
+
# directory, so they stay in the Git checkout together with test_docs.py,
|
|
63
|
+
# which checks them.
|
|
64
|
+
exclude = [
|
|
65
|
+
"web",
|
|
66
|
+
".github",
|
|
67
|
+
"assets",
|
|
68
|
+
".agents",
|
|
69
|
+
".claude",
|
|
70
|
+
".codex",
|
|
71
|
+
"references",
|
|
72
|
+
"tests/test_docs.py",
|
|
73
|
+
]
|
|
61
74
|
|
|
62
75
|
[tool.pytest.ini_options]
|
|
63
76
|
asyncio_mode = "auto"
|
|
@@ -786,6 +786,8 @@ def test_schema_human_format_lists_commands() -> None:
|
|
|
786
786
|
|
|
787
787
|
|
|
788
788
|
def test_fetch_stays_sandboxed_by_default(monkeypatch: Any) -> None:
|
|
789
|
+
# The host can opt out globally; this test checks the built-in default.
|
|
790
|
+
monkeypatch.delenv("WEBSKRAP_CHROMIUM_SANDBOX", raising=False)
|
|
789
791
|
_fake_client(monkeypatch)
|
|
790
792
|
|
|
791
793
|
result = runner.invoke(cli.app, ["fetch", "https://example.test", "--format", "json"])
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: webskrap
|
|
3
|
-
description: Use when writing, debugging, documenting, or reviewing Python scraping, web search, and visual browser automation with WebSkrap. Not for ordinary HTTP clients or search APIs.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# WebSkrap
|
|
7
|
-
|
|
8
|
-
WebSkrap is an async Python scraping package that drives Chromium through its
|
|
9
|
-
own Chrome DevTools Protocol (CDP) client, with no Playwright. The Python API,
|
|
10
|
-
`webskrap` CLI, and `webskrap-mcp` server share core behavior.
|
|
11
|
-
|
|
12
|
-
WebSkrap loads known URLs. To find URLs it loads DuckDuckGo or Bing's
|
|
13
|
-
results page in the same browser and parses the hits. It calls no search API
|
|
14
|
-
and it does not offer Google.
|
|
15
|
-
|
|
16
|
-
## Workflow
|
|
17
|
-
|
|
18
|
-
1. Identify the affected public surface: Python, CLI, MCP, or more than one.
|
|
19
|
-
2. Read its implementation and nearest tests. Trace calls into shared code
|
|
20
|
-
before editing.
|
|
21
|
-
3. Change the shared implementation when behavior belongs to more than one
|
|
22
|
-
surface. Do not patch each wrapper independently.
|
|
23
|
-
4. Update affected public contracts: models, serialization, errors, command
|
|
24
|
-
help, MCP schemas, guides, and tests.
|
|
25
|
-
5. Run focused tests during development, then the full validation gate before
|
|
26
|
-
handoff when practical.
|
|
27
|
-
|
|
28
|
-
## Route by task
|
|
29
|
-
|
|
30
|
-
| Task | Implementation | Tests and contracts |
|
|
31
|
-
| --- | --- | --- |
|
|
32
|
-
| Fetching, sessions, links, or screenshots | `src/webskrap/client.py`, `src/webskrap/models.py` | `tests/test_client_unit.py`, `tests/test_models.py` |
|
|
33
|
-
| Search engines, redirect unwrapping, or results parsing | `src/webskrap/search.py`, `src/webskrap/models.py` | `tests/test_search.py` and its fixtures under `tests/fixtures/search/` |
|
|
34
|
-
| Parsing | `src/webskrap/parsing.py` | `tests/test_parsing.py` |
|
|
35
|
-
| Errors, hints, or exit statuses | `src/webskrap/errors.py`, `src/webskrap/cli_output.py` | `tests/test_errors.py`, CLI and MCP callers |
|
|
36
|
-
| Main CLI commands | `src/webskrap/cli.py` | `tests/test_cli.py`, `tests/test_diagnostics.py` |
|
|
37
|
-
| Persistent browser state | `src/webskrap/browser_session.py`, `src/webskrap/paths.py` | `tests/test_browser_session_unit.py`, `tests/test_paths.py` |
|
|
38
|
-
| Interactive browser commands | `src/webskrap/browser_cli.py`, `src/webskrap/mcp_server.py` | `tests/test_browser_cli.py`, `tests/test_browser_integration.py`, `tests/test_mcp.py` |
|
|
39
|
-
| MCP tools, resources, or annotations | `src/webskrap/mcp_server.py` | `tests/test_mcp.py`, `src/webskrap/guide.md` |
|
|
40
|
-
| Profiles or stealth defaults | `src/webskrap/profiles.py`, `src/webskrap/models.py`, `src/webskrap/client.py` | `tests/test_profiles.py`, opt-in bot-detection tests when needed |
|
|
41
|
-
| Cookie rejection | `src/webskrap/consent.py` | `tests/test_consent.py`; `tests/test_consent_live.py` only for selector rot |
|
|
42
|
-
| Documentation and examples | `README.md`, `src/webskrap/guide.md` | `tests/test_docs.py`, current CLI help, public Python exports |
|
|
43
|
-
|
|
44
|
-
Search for every constructor, serializer, command, and schema that uses a
|
|
45
|
-
changed model or field. Fetch and search behavior cross Python, CLI, and MCP.
|
|
46
|
-
Persistent browser behavior crosses the browser CLI and MCP. Engine markup
|
|
47
|
-
lives only in `src/webskrap/search.py`. When an engine changes its page,
|
|
48
|
-
refresh that engine's fixture and fix that one extractor.
|
|
49
|
-
|
|
50
|
-
## Choose the public surface
|
|
51
|
-
|
|
52
|
-
| Need | Python | CLI | MCP |
|
|
53
|
-
| --- | --- | --- | --- |
|
|
54
|
-
| Fetch one known URL | `client.fetch()` | `webskrap fetch` | `stealth_fetch` |
|
|
55
|
-
| Find URLs for a query | `client.search()` | `webskrap search` | `search` |
|
|
56
|
-
| Preserve cookies or storage | `client.session()` | `webskrap browser open` | `browser_open` |
|
|
57
|
-
| See a rendered page or interact with canvas | `browser_session.view()` on a session page | `webskrap browser view` and `mouse` | `browser_view` and `browser_mouse` |
|
|
58
|
-
| Click, fill, wait, or run a flow | `session.new_page()` and `human_click()` | `webskrap browser` | `browser_interact` and related tools |
|
|
59
|
-
| Diagnose installation | Inspect raised error | `webskrap doctor` | `doctor` |
|
|
60
|
-
|
|
61
|
-
Use a one-shot fetch for one page. Use a persistent browser only when state or
|
|
62
|
-
interaction must survive between operations. Do not repeatedly fetch a page to
|
|
63
|
-
drive a multi-step flow.
|
|
64
|
-
|
|
65
|
-
Read only the reference needed for the task:
|
|
66
|
-
|
|
67
|
-
- [Python API](references/python-api.md): sessions, pages, profile modes, consent, links,
|
|
68
|
-
and fingerprint controls.
|
|
69
|
-
- [CLI](references/cli.md): one-shot fetches, persistent browser commands, and
|
|
70
|
-
Linux ARM64 behavior.
|
|
71
|
-
- [MCP](references/mcp.md): tool selection, confined paths, annotations, and
|
|
72
|
-
keeping tool results small.
|
|
73
|
-
|
|
74
|
-
## Visual browser interaction
|
|
75
|
-
|
|
76
|
-
Use `browser_view` (MCP) or `webskrap browser view --format json` (CLI) when
|
|
77
|
-
layout, images, canvas, or controls missing from the accessibility tree matter.
|
|
78
|
-
MCP returns an image block directly. CLI saves a PNG; open its returned `path`
|
|
79
|
-
with the agent's image-viewing tool before choosing coordinates. A path or
|
|
80
|
-
base64 text alone does not give a model vision. The client and model must
|
|
81
|
-
support image input.
|
|
82
|
-
|
|
83
|
-
The view covers the viewport at one PNG pixel per CSS pixel. Coordinates start
|
|
84
|
-
at its top-left; use the original `width` and `height` if the image viewer
|
|
85
|
-
resizes it. Do not add `scroll_x` or `scroll_y` to mouse coordinates. Full-page
|
|
86
|
-
screenshots are for inspection or export and do not map directly to the mouse.
|
|
87
|
-
|
|
88
|
-
Prefer refs/selectors when they identify a control. Use `browser_mouse` / CLI
|
|
89
|
-
`mouse` for visual targets, then `browser_insert_text` / CLI `insert-text` for
|
|
90
|
-
literal text in the focused control and `browser_press` / CLI `press` for keys.
|
|
91
|
-
Wait for the expected change and take a fresh view after scrolling, navigation,
|
|
92
|
-
or layout changes. Coordinate clicks hit whatever occupies that point.
|
|
93
|
-
Run dependent actions sequentially within a session.
|
|
94
|
-
|
|
95
|
-
This is the browser's rendered page, available headless without Xvfb. It does
|
|
96
|
-
not show browser chrome, OS dialogs, or a desktop stream. One-shot fetch
|
|
97
|
-
sessions are separate; open a persistent browser to drive a visual flow.
|
|
98
|
-
Read the [CLI](references/cli.md) or [MCP](references/mcp.md) reference for examples.
|
|
99
|
-
|
|
100
|
-
## Guardrails
|
|
101
|
-
|
|
102
|
-
- Use public pages, local test servers, or targets the user is authorized to
|
|
103
|
-
access.
|
|
104
|
-
- Do not add CAPTCHA solving, login-wall bypass, credential bypass, or
|
|
105
|
-
access-control circumvention.
|
|
106
|
-
- Do not commit cookies, storage state, proxy credentials, or persistent browser
|
|
107
|
-
data such as `.webskrap/`.
|
|
108
|
-
- Preserve Chromium's sandbox by default. Any opt-out must remain explicit and
|
|
109
|
-
outside page-controlled input.
|
|
110
|
-
- Keep MCP file paths confined to their configured roots. Reject absolute paths,
|
|
111
|
-
traversal, and symlink escapes.
|
|
112
|
-
- Prefer public `webskrap` exports. Use private helpers only in tests.
|
|
113
|
-
- Keep changes typed. Add focused tests for parsing, state transitions, output
|
|
114
|
-
contracts, and tool-safety behavior.
|
|
115
|
-
|
|
116
|
-
## Public contract checklist
|
|
117
|
-
|
|
118
|
-
When public behavior changes, check all applicable items:
|
|
119
|
-
|
|
120
|
-
- Python models and exports
|
|
121
|
-
- CLI arguments, help, JSON output, and exit status
|
|
122
|
-
- MCP input schema, result shape, annotations, and server instructions
|
|
123
|
-
- `README.md` and `src/webskrap/guide.md`
|
|
124
|
-
- unit tests for each affected surface
|
|
125
|
-
|
|
126
|
-
Use the implementation as the source of truth. In particular, read
|
|
127
|
-
`src/webskrap/models.py` for result fields, `src/webskrap/errors.py` for error
|
|
128
|
-
codes and exits, and `src/webskrap/diagnostics.py` for environment reporting.
|
|
129
|
-
Do not copy these catalogs into the skill.
|
|
130
|
-
|
|
131
|
-
## Validation
|
|
132
|
-
|
|
133
|
-
Run the nearest tests while editing. Before handoff, run the repository gate
|
|
134
|
-
when practical:
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
uv run --extra dev pytest -q -m "not browser and not live"
|
|
138
|
-
uv run --extra dev ruff check .
|
|
139
|
-
uv run --extra dev ruff format --check .
|
|
140
|
-
uvx --from pyright==1.1.411 pyright --pythonpath .venv/bin/python src/webskrap
|
|
141
|
-
uv run --extra dev bandit -c pyproject.toml -q -r src/
|
|
142
|
-
uv run --extra dev --with pip-audit==2.9.0 pip-audit
|
|
143
|
-
uv build
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Run browser tests when browser behavior changes and the required browsers are
|
|
147
|
-
installed.
|
|
148
|
-
|
|
149
|
-
Use `WEBSKRAP_LIVE=1 pytest -q -m live` only when explicitly checking public
|
|
150
|
-
third-party behavior. Live tests are non-deterministic and do not replace the
|
|
151
|
-
default suite.
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# CLI
|
|
2
|
-
|
|
3
|
-
Read this reference when changing or using `webskrap` commands. Treat
|
|
4
|
-
`src/webskrap/cli.py` and `src/webskrap/browser_cli.py` as the source of truth
|
|
5
|
-
for arguments and help text.
|
|
6
|
-
|
|
7
|
-
## One-shot commands
|
|
8
|
-
|
|
9
|
-
`webskrap fetch` runs headless Chrome through WebSkrap's own CDP client. `webskrap install`
|
|
10
|
-
downloads Chrome for Testing into the user cache folder
|
|
11
|
-
(`WEBSKRAP_BROWSERS_PATH` overrides it); `--force` downloads it again. `doctor` reports
|
|
12
|
-
readiness, browser identity, host CPU architecture, and Fontconfig family count.
|
|
13
|
-
It warns when the host timezone is UTC. `fetch` and `search` try Chrome, Edge,
|
|
14
|
-
then Chrome for Testing when a channel cannot launch. `schema` describes commands
|
|
15
|
-
as JSON.
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
webskrap doctor --format json
|
|
19
|
-
webskrap schema
|
|
20
|
-
webskrap profiles --format json
|
|
21
|
-
webskrap fetch https://example.com --profile desktop-chrome
|
|
22
|
-
webskrap fetch https://example.com --format json --max-chars 4000
|
|
23
|
-
webskrap fetch https://example.com --format json --links --max-links 20
|
|
24
|
-
webskrap fetch https://example.com --stdout --text-only
|
|
25
|
-
webskrap fetch https://example.com --quiet --output page.html
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Use `--offset` with the previous result's next offset to continue reading a
|
|
29
|
-
long page. Do not re-fetch with an ever larger limit.
|
|
30
|
-
|
|
31
|
-
Pass `--ready-selector '#content'` (CSS or a snapshot ref) to wait for visible content after consent
|
|
32
|
-
dismissal. `--timeout-ms` applies separately to navigation and the readiness wait.
|
|
33
|
-
A missing selector exits 3 (`timeout`). A known challenge or block page
|
|
34
|
-
(DataDome, Cloudflare, Akamai, AWS WAF) exits 11 (`blocked`) without retrying,
|
|
35
|
-
including one served with HTTP 200.
|
|
36
|
-
Ordinary HTTP error pages still return a result with `ok: false`.
|
|
37
|
-
|
|
38
|
-
`webskrap search` loads a results page through the same stealth path and
|
|
39
|
-
prints the organic hits. `--engine` is `bing` (default) or `ddg`.
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
webskrap search "example domain"
|
|
43
|
-
webskrap search "example domain" --engine bing --max-results 5 --format json
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
A `blocked` failure (exit 11) means the engine served a bot challenge; switch
|
|
47
|
-
engine, reuse `--user-data-dir`, or change exit IP instead of retrying.
|
|
48
|
-
|
|
49
|
-
On Linux ARM64, the `chrome` channel may be unavailable. `fetch` retries a
|
|
50
|
-
failed launch with Chrome for Testing. Pass `--channel chromium` to avoid the
|
|
51
|
-
first attempt.
|
|
52
|
-
|
|
53
|
-
`--profile-mode` sets how much of the profile pages see: `native` (default),
|
|
54
|
-
`locale` or `full`. See the Python API reference.
|
|
55
|
-
|
|
56
|
-
## Persistent browser
|
|
57
|
-
|
|
58
|
-
`webskrap browser open` launches detached Chromium. Later commands reconnect
|
|
59
|
-
over CDP, act on its current page, and exit. The browser
|
|
60
|
-
gets the same automation and headless-screen flags as `fetch`; `--headed`
|
|
61
|
-
also drops the `HeadlessChrome` user-agent token. `--proxy URL` routes the
|
|
62
|
-
session through a proxy and defaults WebRTC to `disable_non_proxied_udp`;
|
|
63
|
-
`--webrtc-ip-handling-policy` overrides it. Both are fixed at launch, and
|
|
64
|
-
reopening with different values is refused. The URL never holds credentials:
|
|
65
|
-
for an http or https proxy, set `WEBSKRAP_PROXY_USERNAME` and
|
|
66
|
-
`WEBSKRAP_PROXY_PASSWORD`, and a helper process answers the proxy.
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
webskrap browser open https://example.com --decline-cookies
|
|
70
|
-
webskrap browser snapshot
|
|
71
|
-
webskrap browser click e15
|
|
72
|
-
webskrap browser fill "input[name=q]" "chromium"
|
|
73
|
-
webskrap browser press Enter
|
|
74
|
-
webskrap browser wait --text "Results"
|
|
75
|
-
webskrap browser screenshot page.png --full-page
|
|
76
|
-
webskrap browser screenshot button.png --target e15 # one element
|
|
77
|
-
webskrap browser close
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
`click`, `dblclick` and `type` use human mouse and keystroke timing (wheel
|
|
81
|
-
scrolling, a curved cursor path, a human button hold, spaced keys) and refuse
|
|
82
|
-
a covered target; `fill` sets a value at once.
|
|
83
|
-
|
|
84
|
-
Snapshot refs become stale when the DOM changes. Take another snapshot before
|
|
85
|
-
retrying. `wait` accepts one condition. Do not guess when callers provide more
|
|
86
|
-
than one.
|
|
87
|
-
|
|
88
|
-
Targets are snapshot refs or CSS selectors. Playwright selector engines such
|
|
89
|
-
as `text=` are not supported. Snapshot refs are stable while the element exists,
|
|
90
|
-
also across commands. Iframe content is listed under its `iframe` line. Refs in a frame look
|
|
91
|
-
like `f12345e15`. CSS selectors match in the main frame only.
|
|
92
|
-
|
|
93
|
-
Sessions default to the name `default`. Profiles live below
|
|
94
|
-
`~/.webskrap/browser/<name>/`, unless `WEBSKRAP_BROWSER_DIR` changes the root.
|
|
95
|
-
They contain cookies and logins and must remain mode `0700`.
|
|
96
|
-
|
|
97
|
-
Keep Chromium's OS sandbox enabled. The explicit opt-outs are
|
|
98
|
-
`webskrap browser open --no-sandbox` for one session,
|
|
99
|
-
`webskrap fetch --no-sandbox` / `webskrap search --no-sandbox` for one-shot
|
|
100
|
-
calls, and `WEBSKRAP_CHROMIUM_SANDBOX=0` for a host. Document that disabling it
|
|
101
|
-
leaves a compromised renderer uncontained. Never pass `--no-sandbox` (or its
|
|
102
|
-
companions) through `--launch-arg`: those flags are blocked.
|
|
103
|
-
|
|
104
|
-
Commands act on the current tab. `tab-list`, `tab-new [url]`,
|
|
105
|
-
`tab-select <index>` and `tab-close [index]` manage tabs; a popup or a new tab
|
|
106
|
-
becomes current. Current limitations are Chrome for Testing only, no network
|
|
107
|
-
mocking, tracing, or video, and history navigation that reloads.
|
|
108
|
-
|
|
109
|
-
## Vision and coordinate input
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
webskrap browser open https://example.com -s visual
|
|
113
|
-
webskrap browser view viewport.png -s visual --format json
|
|
114
|
-
# Inspect the PNG with your image-viewing tool before choosing coordinates.
|
|
115
|
-
webskrap browser mouse click 120 80 -s visual
|
|
116
|
-
webskrap browser insert-text "hello 世界" -s visual
|
|
117
|
-
webskrap browser press Enter -s visual
|
|
118
|
-
webskrap browser mouse scroll 400 300 --delta-y 500 -s visual
|
|
119
|
-
webskrap browser view viewport.png -s visual --format json
|
|
120
|
-
# Inspect the new view before choosing the drag points.
|
|
121
|
-
webskrap browser mouse drag 100 150 --end-x 250 --end-y 180 -s visual
|
|
122
|
-
webskrap browser view viewport.png -s visual --format json
|
|
123
|
-
webskrap browser close -s visual
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
`view` writes a viewport PNG and returns `path` (absolute), `url`, `title`,
|
|
127
|
-
`width`, `height`, `scroll_x`, `scroll_y`, `mime_type`, and `coordinate_system`
|
|
128
|
-
(`viewport-css-pixels`). Omitting the path generates a unique PNG in the current
|
|
129
|
-
directory. JSON contains metadata, not base64 image data: the agent must open
|
|
130
|
-
the file with an image-viewing tool.
|
|
131
|
-
|
|
132
|
-
One image pixel equals one CSS pixel, even on high-DPI pages. `(0, 0)` is the
|
|
133
|
-
viewport's top-left. Map resized previews back to the original dimensions;
|
|
134
|
-
do not add document scroll offsets. `screenshot --full-page` remains available
|
|
135
|
-
for export, but its coordinates cannot be used directly with `mouse`.
|
|
136
|
-
|
|
137
|
-
`mouse` accepts `click`, `dblclick`, `move`, `scroll`, or `drag`, with required
|
|
138
|
-
`x y`. `--button` is `left` (default), `middle`, or `right` for click/double-click/
|
|
139
|
-
drag. Drag requires both `--end-x` and `--end-y`. Scroll uses `--delta-x` and
|
|
140
|
-
`--delta-y` over the element under `x y`; positive vertical deltas scroll down,
|
|
141
|
-
including in nested scroll areas. Non-finite, out-of-viewport, and unused
|
|
142
|
-
arguments fail with `usage`. Coordinate input uses direct browser events;
|
|
143
|
-
selector-based `click` and `type` retain their human timing and covered-target
|
|
144
|
-
checks.
|
|
145
|
-
|
|
146
|
-
`insert-text` inserts literal Unicode into the focused control with an input
|
|
147
|
-
event. It does not interpret `Enter` as a key or emit per-character keydown/
|
|
148
|
-
keyup events. Focus with a click or Tab; use `press` for shortcuts and keys,
|
|
149
|
-
or selector-based `type` for controls that require individual keystrokes.
|
|
150
|
-
|
|
151
|
-
Take a fresh view after navigation, scrolling, or layout changes. Wheel input
|
|
152
|
-
may return before scrolling finishes; wait for the expected page condition.
|
|
153
|
-
Prefer refs/selectors for known controls, and run dependent session actions
|
|
154
|
-
sequentially. This shows the page viewport, not browser chrome or OS dialogs.
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
# MCP
|
|
2
|
-
|
|
3
|
-
Read this reference for MCP tools, resources, schemas, annotations, and safety
|
|
4
|
-
boundaries. Confirm the current contract in `src/webskrap/mcp_server.py` and
|
|
5
|
-
`tests/test_mcp.py`.
|
|
6
|
-
|
|
7
|
-
## Select a tool
|
|
8
|
-
|
|
9
|
-
- `stealth_fetch` is the default one-shot fetch. It exposes fingerprint,
|
|
10
|
-
WebRTC, user-agent, and persistent-profile controls.
|
|
11
|
-
- `fetch` is the simpler one-shot form.
|
|
12
|
-
- `search` finds URLs for a query on Bing (default) or DuckDuckGo, with the
|
|
13
|
-
same stealth controls as `stealth_fetch`. Google is not offered. Follow up
|
|
14
|
-
with `stealth_fetch` on the hits worth reading.
|
|
15
|
-
- `doctor` reports readiness, browser identity, CPU architecture, Fontconfig
|
|
16
|
-
family count, versions, paths, environment overrides, persistent sessions,
|
|
17
|
-
and the host timezone, with a warning when it is UTC.
|
|
18
|
-
- `browser_open` and the other `browser_*` tools drive a stateful flow.
|
|
19
|
-
`browser_open` takes `webrtc_ip_handling_policy` but no proxy; open a proxied
|
|
20
|
-
session with `webskrap browser open --proxy URL` and the tools reuse it.
|
|
21
|
-
- `webskrap://guide`, `webskrap://profiles`, and `webskrap://sessions` expose
|
|
22
|
-
static guidance and current state as resources.
|
|
23
|
-
|
|
24
|
-
One-shot fetches do not share cookies between calls. Use a browser session when
|
|
25
|
-
state must persist.
|
|
26
|
-
|
|
27
|
-
All tools drive Chromium through WebSkrap's own CDP client. Selectors are
|
|
28
|
-
snapshot refs or CSS. `stealth_fetch` takes `profile_mode` (`native`, `locale`
|
|
29
|
-
or `full`) to set how much of the profile pages see.
|
|
30
|
-
|
|
31
|
-
## Keep results small
|
|
32
|
-
|
|
33
|
-
- `max_chars` defaults to 8000 for fetch, snapshot, text, and eval tools.
|
|
34
|
-
Continue with `offset` and `next_text_offset` on stable page text. Fetch
|
|
35
|
-
windows navigate again; use `browser_text` in a session to avoid re-fetching.
|
|
36
|
-
- `browser_text` reads visible body text and selects the window in the browser,
|
|
37
|
-
avoiding a full accessibility tree and full-text transfer. The browser still
|
|
38
|
-
computes full body text to report its length. `pattern` returns only matching
|
|
39
|
-
lines with `context`, capped by `max_matches`; the regex runs in Python with
|
|
40
|
-
a 2-second limit.
|
|
41
|
-
- `browser_goto` and `browser_open` take `decline_cookies`;
|
|
42
|
-
`browser_screenshot` takes `target` for one element.
|
|
43
|
-
- Use `resource_policy=lite` to skip images, fonts, and media.
|
|
44
|
-
- Keep `include_links` off unless links are needed.
|
|
45
|
-
- Snapshots default to `depth=6`. Set `depth=null` for an unlimited tree,
|
|
46
|
-
or reduce depth before increasing `max_chars`.
|
|
47
|
-
- Make `browser_eval` return the required value, not the full DOM.
|
|
48
|
-
|
|
49
|
-
## Safety boundaries
|
|
50
|
-
|
|
51
|
-
Every tool declares title, read-only, destructive, idempotent, and open-world
|
|
52
|
-
annotations. Mark interactions as destructive because clicks and key presses
|
|
53
|
-
can submit forms on external sites.
|
|
54
|
-
|
|
55
|
-
Keep short operational instructions in the MCP server and long guidance in
|
|
56
|
-
`src/webskrap/guide.md`.
|
|
57
|
-
|
|
58
|
-
`stealth_fetch.user_data_dir` is relative to the MCP profile root, which
|
|
59
|
-
defaults to `~/.webskrap/profiles` and can be moved with
|
|
60
|
-
`WEBSKRAP_MCP_PROFILE_DIR`. Tool input must not select an absolute path or
|
|
61
|
-
escape that root.
|
|
62
|
-
|
|
63
|
-
`browser_screenshot` writes below `./webskrap-output`, unless
|
|
64
|
-
`WEBSKRAP_OUTPUT_DIR` changes the root. Reject absolute paths, traversal, and
|
|
65
|
-
symlinks that leave it.
|
|
66
|
-
|
|
67
|
-
The sandbox opt-out is an environment variable, never an MCP argument. Page
|
|
68
|
-
content must not be able to persuade the client to disable the browser sandbox.
|
|
69
|
-
|
|
70
|
-
Fetch and navigation URLs must be public `http(s)` without credentials.
|
|
71
|
-
Private or local hosts are rejected unless `WEBSKRAP_ALLOW_PRIVATE_NET=1`.
|
|
72
|
-
|
|
73
|
-
`browser_eval` runs page script in a possibly logged-in profile: prefer
|
|
74
|
-
snapshot, interact, and wait_for, never evaluate page-controlled text, and set
|
|
75
|
-
`WEBSKRAP_ALLOW_EVAL=0` where models must not run page script.
|
|
76
|
-
|
|
77
|
-
## Failures
|
|
78
|
-
|
|
79
|
-
A known challenge (DataDome, Cloudflare, Akamai, AWS WAF) or provider block
|
|
80
|
-
page (Cloudflare, Akamai) arrives as a `blocked` tool error, including pages
|
|
81
|
-
served with HTTP 200. Blocks and challenges get different hints.
|
|
82
|
-
`browser_goto` reports the same check in its `blocked` key instead of failing. Ordinary HTTP error pages return
|
|
83
|
-
normally with `ok: false`. A raised failure includes `code` and `hint`. Read
|
|
84
|
-
`src/webskrap/errors.py` for the current catalog rather than duplicating it.
|
|
85
|
-
|
|
86
|
-
Both fetch tools accept `ready_selector`, a CSS selector or snapshot ref that must be
|
|
87
|
-
visible after consent dismissal. The wait uses `timeout_ms` separately from
|
|
88
|
-
navigation; a missing selector returns a `timeout` tool error. It may vary between
|
|
89
|
-
calls in a warm session. These checks do not establish that later interactions
|
|
90
|
-
will remain free of challenges.
|
|
91
|
-
|
|
92
|
-
## Vision and coordinate input
|
|
93
|
-
|
|
94
|
-
`browser_view(session="visual")` returns an inline `image/png` image block,
|
|
95
|
-
a text block with metadata, and the same metadata in `structuredContent`:
|
|
96
|
-
`url`, `title`, `width`, `height`, `scroll_x`, `scroll_y`, `mime_type`, and
|
|
97
|
-
`coordinate_system: "viewport-css-pixels"`. It writes no file. Use it with an
|
|
98
|
-
image-capable model and client; `browser_screenshot` remains the file/export tool.
|
|
99
|
-
|
|
100
|
-
Open with `browser_open(session="visual", url=...)`, inspect
|
|
101
|
-
`browser_view(session="visual")`, then call:
|
|
102
|
-
|
|
103
|
-
```json
|
|
104
|
-
{"action": "click", "x": 120, "y": 80, "session": "visual"}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
with `browser_mouse`. Choose coordinates from the observed target. For a focused
|
|
108
|
-
input, `browser_insert_text(text="hello 世界", session="visual")` inserts literal
|
|
109
|
-
text; `browser_press(key="Enter", session="visual")` sends a key. Insertion emits
|
|
110
|
-
an input event, not individual keydown/keyup events. Use `browser_interact` with
|
|
111
|
-
`action="type"` when a control requires individual keystrokes.
|
|
112
|
-
|
|
113
|
-
Mouse actions are `click`, `dblclick`, `move`, `scroll`, and `drag`. All require
|
|
114
|
-
`x` and `y`. `button` is `left` (default), `middle`, or `right`, for click,
|
|
115
|
-
double-click, or drag only. Drag also requires `end_x` and `end_y`. Scroll accepts
|
|
116
|
-
`delta_x` and `delta_y` in CSS pixels, targeting the element under the pointer;
|
|
117
|
-
positive `delta_y` scrolls down. Invalid, non-finite, unused, or out-of-viewport
|
|
118
|
-
arguments return a `usage` error before mouse input is sent.
|
|
119
|
-
|
|
120
|
-
Use the original PNG dimensions: each image pixel is one CSS pixel, independent
|
|
121
|
-
of device scale. `(0,0)` is the viewport's top-left. Map any resized preview back
|
|
122
|
-
to `width`/`height`, and do not add scroll offsets. Full-page screenshots do not
|
|
123
|
-
share this mouse coordinate mapping.
|
|
124
|
-
|
|
125
|
-
Prefer refs/selectors for identifiable controls. Coordinate clicks hit whatever
|
|
126
|
-
is visible at the point. After an action, wait for the expected change, then take
|
|
127
|
-
a fresh view before choosing more coordinates. Wheel input can finish after the
|
|
128
|
-
call returns. Run dependent actions sequentially in each session. The image
|
|
129
|
-
shows the page, including canvas and frames, not browser chrome, OS dialogs,
|
|
130
|
-
or a live desktop stream. Headless rendering needs no Xvfb.
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Python API
|
|
2
|
-
|
|
3
|
-
Read this reference for Python API work. Confirm signatures and defaults in
|
|
4
|
-
`src/webskrap/client.py` and `src/webskrap/models.py` before changing code.
|
|
5
|
-
|
|
6
|
-
## Fetch or session
|
|
7
|
-
|
|
8
|
-
Use `WebSkrapClient` as an async context manager. `client.fetch()` handles a
|
|
9
|
-
one-shot request. Use `client.session()` when cookies, storage, manual page
|
|
10
|
-
work, headed debugging, or interactions must persist.
|
|
11
|
-
|
|
12
|
-
```python
|
|
13
|
-
import asyncio
|
|
14
|
-
|
|
15
|
-
from webskrap import WebSkrapClient
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
async def main() -> None:
|
|
19
|
-
async with WebSkrapClient() as client:
|
|
20
|
-
result = await client.fetch("https://example.com", include_links=True)
|
|
21
|
-
print(result.status, result.final_url, result.title)
|
|
22
|
-
print(result.text[:200])
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
asyncio.run(main())
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
WebSkrap 3 drives Chromium through its own CDP client. There is no driver to
|
|
29
|
-
choose. Pick the browser with `channel` (`chrome`, `msedge`, `chromium`):
|
|
30
|
-
|
|
31
|
-
```python
|
|
32
|
-
from pathlib import Path
|
|
33
|
-
|
|
34
|
-
from webskrap import SessionConfig
|
|
35
|
-
|
|
36
|
-
config = SessionConfig(
|
|
37
|
-
channel="chrome",
|
|
38
|
-
headless=True,
|
|
39
|
-
user_data_dir=Path(".webskrap/headless-profile"),
|
|
40
|
-
)
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
`channel=None` or `"chromium"` uses the Chrome for Testing build from
|
|
44
|
-
`webskrap install`.
|
|
45
|
-
|
|
46
|
-
## Pages you drive yourself
|
|
47
|
-
|
|
48
|
-
`session.new_page()` returns a `webskrap.cdp.CDPPage` with the session's
|
|
49
|
-
profile and policies applied. Close it with `session.close_page(page)`.
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
async with WebSkrapClient() as client:
|
|
53
|
-
session = await client.session("manual")
|
|
54
|
-
page = await session.new_page()
|
|
55
|
-
response = await page.goto("https://example.com", wait_until="load", timeout_s=30)
|
|
56
|
-
title = await page.evaluate("document.title")
|
|
57
|
-
await session.human_click(page, "a")
|
|
58
|
-
await session.close_page(page)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
- `page.goto()` returns the CDP `Response` object, for example `status` and
|
|
62
|
-
`headers`.
|
|
63
|
-
- `page.evaluate()` runs in an isolated world. It shares the DOM with the page
|
|
64
|
-
but not the page's JavaScript globals.
|
|
65
|
-
- `session.human_click()` and `session.human_type()` take a CSS selector or a
|
|
66
|
-
snapshot ref.
|
|
67
|
-
- `page.send(method, params)` sends any other CDP command to the page.
|
|
68
|
-
|
|
69
|
-
## Consent and links
|
|
70
|
-
|
|
71
|
-
`client.fetch()` and `session.fetch()` accept `ready_selector`, an optional
|
|
72
|
-
CSS selector or snapshot ref that must become visible after consent dismissal and before
|
|
73
|
-
extraction. Its wait uses `timeout_ms` or `navigation_timeout_ms` separately from
|
|
74
|
-
navigation. A missing selector raises a timeout. A blank selector is a usage error.
|
|
75
|
-
|
|
76
|
-
Known challenge pages (DataDome, Cloudflare, Akamai, AWS WAF) and provider block pages (Cloudflare, Akamai) raise `WebSkrapError` with
|
|
77
|
-
`code=ErrorCode.BLOCKED`, even when served with HTTP 200. `error.hint`
|
|
78
|
-
holds a kind-specific hint. Ordinary HTTP errors
|
|
79
|
-
still return `FetchResult(ok=False)`. Detection uses active provider markers and
|
|
80
|
-
headers; it does not identify every challenge or solve verification widgets.
|
|
81
|
-
|
|
82
|
-
Cookie rejection is opt-in for Python callers. The CLI and MCP server enable it
|
|
83
|
-
by default.
|
|
84
|
-
|
|
85
|
-
```python
|
|
86
|
-
config = SessionConfig(decline_cookies=True, decline_cookies_timeout_ms=2_000)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
`FetchResult.cookie_notice_declined` reports `"cmp"`, `"text"`, or `None`.
|
|
90
|
-
`session.decline_cookies(page)` applies the same handling to a page controlled
|
|
91
|
-
by the caller.
|
|
92
|
-
|
|
93
|
-
Link collection is opt-in with `include_links=True` and capped by `max_links`.
|
|
94
|
-
Links resolve against the final URL. Duplicate and `javascript:` targets are
|
|
95
|
-
dropped. `links_total` is the count before the cap. A session with
|
|
96
|
-
`java_script_enabled=False` cannot collect links.
|
|
97
|
-
|
|
98
|
-
## Search
|
|
99
|
-
|
|
100
|
-
`client.search(query, engine=..., max_results=...)` and
|
|
101
|
-
`session.search(...)` load the engine's results page through the same fetch
|
|
102
|
-
path, so `SessionConfig` proxy, consent, and profile settings apply. Engines
|
|
103
|
-
are `SearchEngine.BING` (default) and `SearchEngine.DDG`; Google is not
|
|
104
|
-
offered.
|
|
105
|
-
|
|
106
|
-
```python
|
|
107
|
-
from webskrap import SearchEngine, WebSkrapClient
|
|
108
|
-
|
|
109
|
-
async with WebSkrapClient() as client:
|
|
110
|
-
result = await client.search("example domain", engine=SearchEngine.BING, max_results=5)
|
|
111
|
-
for hit in result.hits:
|
|
112
|
-
print(hit.title, hit.url, hit.snippet)
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
`SearchResult.hits` is capped by `max_results`; `hits_total` counts the page.
|
|
116
|
-
Destinations are unwrapped from the engine's click redirects and deduplicated.
|
|
117
|
-
A bot challenge raises `WebSkrapError` with code `blocked`; nothing is retried.
|
|
118
|
-
|
|
119
|
-
## Detection-sensitive pages
|
|
120
|
-
|
|
121
|
-
Headed Chrome is the strongest available mode for strict detection surfaces:
|
|
122
|
-
|
|
123
|
-
```python
|
|
124
|
-
config = SessionConfig(channel="chrome", headless=False)
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
For fingerprint-statistics or WebRTC leak-test pages, prefer native browser
|
|
128
|
-
controls over JavaScript patches:
|
|
129
|
-
|
|
130
|
-
```python
|
|
131
|
-
config = SessionConfig(
|
|
132
|
-
channel="chrome",
|
|
133
|
-
headless=True,
|
|
134
|
-
profile_mode="locale",
|
|
135
|
-
reduce_fingerprint_surface=True,
|
|
136
|
-
virtual_display=True,
|
|
137
|
-
webrtc_ip_handling_policy="disable_non_proxied_udp",
|
|
138
|
-
)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
`profile_mode` sets how much of the `BrowserProfile` pages see:
|
|
142
|
-
|
|
143
|
-
- `"native"` (default): nothing. The browser shows its own identity.
|
|
144
|
-
- `"locale"`: locale, timezone, color scheme, reduced motion and extra headers.
|
|
145
|
-
On Linux, locale and timezone come from the browser environment.
|
|
146
|
-
- `"full"`: also viewport, screen, device scale, touch and user agent, as
|
|
147
|
-
WebSkrap 2.x did. Each override is a surface that a page can cross-check.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
../../../.agents/skills/webskrap/SKILL.md
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
../../../.agents/skills/webskrap/SKILL.md
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""Guard the skill's routing without duplicating generated API catalogs."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
ROOT = Path(__file__).resolve().parents[1]
|
|
8
|
-
SKILL_DIR = ROOT / ".agents" / "skills" / "webskrap"
|
|
9
|
-
SKILL = (SKILL_DIR / "SKILL.md").read_text(encoding="utf-8")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def test_skill_routes_to_existing_references() -> None:
|
|
13
|
-
for relative_path in (
|
|
14
|
-
"references/python-api.md",
|
|
15
|
-
"references/cli.md",
|
|
16
|
-
"references/mcp.md",
|
|
17
|
-
):
|
|
18
|
-
assert f"]({relative_path})" in SKILL
|
|
19
|
-
assert (SKILL_DIR / relative_path).is_file()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_agent_skill_folders_share_one_copy() -> None:
|
|
23
|
-
for agent in (".claude", ".codex"):
|
|
24
|
-
folder = ROOT / agent / "skills" / "webskrap"
|
|
25
|
-
assert (folder / "SKILL.md").resolve() == (SKILL_DIR / "SKILL.md").resolve()
|
|
26
|
-
assert (folder / "references").resolve() == (ROOT / "references").resolve()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def test_skill_routes_dynamic_contracts_to_their_sources() -> None:
|
|
30
|
-
for source in (
|
|
31
|
-
"src/webskrap/models.py",
|
|
32
|
-
"src/webskrap/errors.py",
|
|
33
|
-
"src/webskrap/diagnostics.py",
|
|
34
|
-
"src/webskrap/cli.py",
|
|
35
|
-
"src/webskrap/browser_cli.py",
|
|
36
|
-
"src/webskrap/mcp_server.py",
|
|
37
|
-
):
|
|
38
|
-
assert f"`{source}`" in SKILL
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_skill_states_how_search_works_and_what_it_excludes() -> None:
|
|
42
|
-
assert "calls no search API" in SKILL
|
|
43
|
-
assert "does not offer Google" in SKILL
|
|
44
|
-
assert "`src/webskrap/search.py`" in SKILL
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|