Learn Scrapling - StealthyFetcher (Undetectable Browsing)
Episode 8 of 23

Learn Scrapling - StealthyFetcher (Undetectable Browsing)

Dissecting StealthyFetcher: browser automation via the Chrome DevTools Protocol with built-in anti-detection, understanding why plain requests are easily detected, bypassing Cloudflare and Turnstile out of the box via solve_cloudflare, and anti-fingerprint features like block_webrtc and hide_canvas.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 7 you got PlayWrightFetcher in hand for rendering JavaScript. But there's a category of websites that a regular browser isn't enough to get through: those guarded by Cloudflare, Turnstile, or JavaScript challenges. That's where StealthyFetcher comes in — a fetcher designed specifically for undetectable browsing. This is one of Scrapling's most famous features, and this episode 8 will dissect it from the bottom up.

You'll learn why plain requests are easily detected, how StealthyFetcher works on top of the Chrome DevTools Protocol with anti-detection modifications, how it bypasses Cloudflare and Turnstile out of the box via solve_cloudflare, anti-fingerprint features like block_webrtc and hide_canvas, session usage for many pages, and when to use StealthyFetcher ethically. This is the peak of the basic fetching phase in our series.

Why Plain Requests Are Easily Detected

Websites behave like doormen. Every incoming request is checked from many angles, and plain requests like requests.get(url) are caught by several telltale signs at once. First, the TLS fingerprint — the way a library sends its TLS handshake has a unique pattern different from a real browser, and this can be recognized even before HTTP begins. Second, incomplete headers — browsers send dozens of headers in a certain order; requests only sends a few, and in a different order.

Third, browser behavior — properties like navigator.webdriver, screen size, GPU, canvas fingerprint, and WebGL differ on automated browsers. Fourth, request rhythm — bots tend to bombard without natural pauses. Modern anti-bot systems like Cloudflare combine all these signals into a risk score; once the score crosses a threshold, you're greeted with a challenge page or a 403 status. That's why the same URL can be accessible to humans yet reject bots.

Getting to Know StealthyFetcher

StealthyFetcher is Scrapling's answer to the problem above. It controls a real browser via the Chrome DevTools Protocol (CDP) — the same protocol DevTools uses to communicate with the browser — then injects dozens of anti-detection modifications so an automated browser can't be distinguished from a normal one. Using it is as easy as any other fetcher:

Pythonstealthy-basic.py
from scrapling import StealthyFetcher
 
page = StealthyFetcher.fetch(
    "https://example.com/proteksi",
    headless=True,
    network_idle=True,
)
print(page.status)
print(page.css("h1::text").get())

StealthyFetcher.fetch{python} opens a browser, runs all the anti-detection scripts, loads the page, and returns a Response object you can select as usual. Note that headless=True still works — stealth doesn't depend on showing a window, but on fingerprint modifications. For debugging or very strict cases, headless=False is available as an option.

Info

StealthyFetcher uses a specially modified browser (a Chromium/Firefox derivative with anti-detection patches). Make sure you've run scrapling install to download this browser along with its dependencies before the first use.

CDP and Anti-Detection Mechanisms

Here's a quick look at how StealthyFetcher "disguises" a browser. Through CDP, it replays all the signals anti-bot systems use to judge you. Those signals include:

  • Navigator fingerprint — disguises navigator.webdriver, language, platform, and other properties to be consistent with a human browser.
  • Canvas and WebGL — adds random noise to canvas operations so the generated fingerprint isn't identical across sessions.
  • WebRTC — prevents local IP address leaks through peer-to-peer connections.
  • User agent and headers — imitates a real browser with complete, realistic headers.

All these modifications work automatically; you don't need to configure anything for basic use. For deeper control, Scrapling exposes options like block_webrtc=True to block WebRTC, hide_canvas=True to add canvas noise, and humanize=True to make browser interactions feel more human.

Pythonstealthy-features.py
from scrapling import StealthyFetcher
 
page = StealthyFetcher.fetch(
    "https://example.com/ketat",
    headless=True,
    network_idle=True,
    block_webrtc=True,
    hide_canvas=True,
    humanize=True,
)

The combination above — blocking WebRTC, adding canvas noise, and imitating human behavior — closes the last gaps that anti-bot systems typically use to detect automation. Start from the default options, and enable extra features only when the target website starts suspecting you.

Cloudflare & Turnstile: Out-of-the-Box Bypass

This is StealthyFetcher's trump card: the ability to bypass Cloudflare protection — including Turnstile — without complicated configuration. solve_cloudflare=True makes the fetcher detect and solve three types of Cloudflare challenges: JavaScript challenges, interactive challenges (verification checkbox), and invisible challenges that run in the background.

Pythonsolve-cloudflare.py
from scrapling import StealthyFetcher
 
page = StealthyFetcher.fetch(
    "https://nopecha.com/demo/cloudflare",
    headless=True,
    solve_cloudflare=True,
)
print(page.status)

With solve_cloudflare=True{python}, the fetcher waits until the challenge is resolved and the real page loads before returning the response. Even more interesting, this option is also available from the command line for quick experiments without writing code:

stealthy-cli.sh
scrapling extract stealthy-fetch \
  "https://nopecha.com/demo/cloudflare" \
  hasil.html \
  --solve-cloudflare \
  --css-selector "body"

The command above runs a stealth fetch via the CLI, solves the Cloudflare challenge, then saves the result to the hasil.html file. The --css-selector{bash} flag limits the output to only the elements you're looking for. This kind of CLI is very useful for testing whether a URL can be bypassed before you build a full spider.

Danger

The ability to bypass Cloudflare is a double-edged sword. Use it only on websites you own, public websites that allow scraping, or for clearly defined research purposes. Bypassing protections to extract data in violation of terms of service or the law can lead to permanent bans and even lawsuits. Always respect robots.txt and Terms of Service.

Sessions: Many Pages, One Browser

Every time StealthyFetcher.fetch is called, a browser is opened and closed — that's expensive. To fetch many pages from a protected domain, use a session so the browser stays alive. This also keeps Cloudflare cookies warm, so the challenge doesn't need to be re-solved for every page.

Pythonstealthy-session.py
from scrapling.fetchers import StealthySession
 
with StealthySession(headless=True, solve_cloudflare=True) as session:
    for n in range(1, 6):
        page = session.fetch(f"https://example.com/proteksi/hal/{n}", network_idle=True)
        print(n, page.status)
 
page = session.fetch("https://example.com/proteksi/setelah-login")

StealthySession{python} keeps the browser, cookies, and challenge state in one session. After the first challenge is resolved, subsequent pages usually load without friction — which is why combining a session with solve_cloudflare is the most common production pattern. There's also an async variant, AsyncStealthySession(max_pages=4), that manages a pool of browser tabs for parallel contexts.

When to Use StealthyFetcher

StealthyFetcher isn't the answer to everything. The rule is simple: use the lightest fetcher that can get through the target. Start with the HTTP Fetcher — fast and cheap. If the page needs JavaScript, move up to PlayWrightFetcher. Only when the website shows hard protection like Cloudflare, Turnstile, or fingerprint-based blocking should you call in StealthyFetcher. This layered approach keeps speed and resources optimal — a stealth browser is far heavier than plain HTTP, so don't use it for pages that are actually bot-friendly.

Closing

Episode 8 completes the basic fetching phase. You now understand why plain requests get detected (TLS fingerprint, headers, behavior), how StealthyFetcher works on top of the Chrome DevTools Protocol with anti-detection modifications, how to bypass Cloudflare and Turnstile via solve_cloudflare=True both in Python and the CLI, using StealthySession for many pages, and the strategy of choosing a fetcher from the lightest to the most stealthy.

The key takeaways:

  • Plain requests are detected by TLS fingerprint, incomplete headers, and non-human behavior.
  • StealthyFetcher controls a browser via CDP and injects automatic anti-detection.
  • solve_cloudflare=True resolves JavaScript, interactive, and invisible challenges.
  • The CLI scrapling extract stealthy-fetch with --solve-cloudflare is useful for quick testing.
  • StealthySession keeps cookies and challenge state so subsequent pages load quickly.

In episode 9 we move from the fetching side to the adaptive parsing side: auto-match and adaptive selectors — selectors that can adjust themselves when a website's DOM structure changes, without the help of AI. See you there!

Learn Scrapling - StealthyFetcher (Undetectable Browsing) | Learn Scrapling