@@ -150,6 +150,48 @@ async def main() -> None:
150150asyncio.run(main())
151151```
152152
153+ ## Auto-decline cookie banners
154+
155+ Every ` session.fetch() ` (and therefore the CLI and the MCP tools) clicks the
156+ reject control of a cookie consent notice before reading the page, so scraped
157+ text is not buried under a banner and no optional cookies are accepted.
158+
159+ Detection is inspired by Brave's
160+ [ cookiecrumbler] ( https://github.com/brave/cookiecrumbler ) , which finds consent
161+ notices; WebSkrap dismisses them. Two strategies, tried in every frame:
162+
163+ 1 . Reject buttons of the common consent platforms (OneTrust, Cookiebot, Didomi,
164+ Usercentrics, Sourcepoint, Quantcast, Osano, Complianz, CookieYes, ...).
165+ 2 . A clickable whose label matches a reject phrase ("Reject all", "Refuser
166+ tout", "Ablehnen", "Continue without accepting", ...), scoped to a
167+ cookie/consent container so unrelated "Decline" buttons are never touched.
168+
169+ ` FetchResult.cookie_notice_declined ` reports which strategy clicked (` "cmp" ` ,
170+ ` "text" ` , or ` None ` ).
171+
172+ ``` python
173+ config = SessionConfig(
174+ decline_cookies = True , # default
175+ decline_cookies_timeout_ms = 2_000 , # wait for a late-injected notice; 0 = check once
176+ )
177+
178+ result = await client.fetch(" https://example.com" , config = config)
179+ print (result.cookie_notice_declined)
180+ ```
181+
182+ The timeout is the per-fetch cost on pages without a notice. Pages that carry a
183+ consent-platform iframe are retried for up to 3s while it renders. For pages you
184+ drive yourself, call it directly:
185+
186+ ``` python
187+ page = await session.context.new_page()
188+ await page.goto(" https://example.com" , wait_until = " domcontentloaded" )
189+ await session.decline_cookies(page)
190+ ```
191+
192+ Consent walls with no reject control on the first layer (pay-or-consent) are
193+ left alone.
194+
153195## Custom profile
154196
155197``` python
@@ -179,6 +221,8 @@ config = SessionConfig(
179221 storage_state = None ,
180222 proxy = ProxyConfig(server = " http://127.0.0.1:8080" ),
181223 resource_policy = ResourcePolicy.LITE ,
224+ decline_cookies = True ,
225+ decline_cookies_timeout_ms = 2_000 ,
182226 ignore_https_errors = True ,
183227 java_script_enabled = True ,
184228 service_workers = " allow" ,
@@ -375,6 +419,8 @@ webskrap fetch https://example.com --profile desktop-chrome
375419webskrap fetch https://example.com --format json --max-chars 12000
376420webskrap fetch https://example.com --stdout --text-only
377421webskrap fetch https://example.com --screenshot example.png
422+ webskrap fetch https://example.com --no-decline-cookies
423+ webskrap fetch https://example.com --decline-cookies-timeout-ms 4000
378424webskrap fetch https://example.com --channel chrome \
379425 --user-data-dir .webskrap/headless-profile
380426webskrap fetch https://amiunique.org/fr/fingerprint \
@@ -405,8 +451,10 @@ clean visible page text by default, with no HTML tags, scripts, or style noise,
405451so agents spend tokens on content, not markup (typically 5-10x fewer tokens than
406452raw HTML). Pass ` text_only=False ` when you actually need the HTML. Use
407453` stealth_fetch ` for finer control over fingerprint surface, WebRTC, UA masking,
408- and persistent profiles. Every result carries ` status ` , ` final_url ` , ` title ` ,
409- ` text_length ` , and truncation flags so the model knows exactly what it got.
454+ and persistent profiles. Both auto-decline cookie banners (` decline_cookies=False `
455+ to keep them). Every result carries ` status ` , ` final_url ` , ` title ` ,
456+ ` text_length ` , ` cookie_notice_declined ` , and truncation flags so the model knows
457+ exactly what it got.
410458
411459``` bash
412460pip install webskrap
0 commit comments