首页
/ Scrapling StealthyFetcher 实战:绕过 Cloudflare 验证与浏览器指纹检测的完整指南

Scrapling StealthyFetcher 实战:绕过 Cloudflare 验证与浏览器指纹检测的完整指南

2026-09-04 13:40:24作者:钟日瑜

本文以 Scrapling 官方文档 docs/fetching/stealthy.md 为主体,系统讲解 StealthyFetcher 类的定位、全部参数、Cloudflare 自动求解、浏览器自动化与 Session 池化用法,并结合 scrapling/engines/_browsers/_stealth.pyscrapling/engines/_browsers/_base.py 等源码,还原每个反检测参数(WebRTC、Canvas、WebGL)在底层如何转化为浏览器启动标志,以及 Cloudflare 求解器内部真实的检测与点击逻辑。读完后你应能独立完成高防护网站的抓取、配置可复用的隐身会话,并理解 Scrapling 在 patchright 之上做了哪些自动化的隐身处理。

StealthyFetcher 的定位与前置知识

StealthyFetcher 是 Scrapling 提供的隐身(stealth)抓取器。它与 DynamicFetcher 类非常相似——相同的浏览器(Chromium/Google Chrome)、相同的自动化方式、同样基于 Playwright 的 API 体系。核心区别在于:StealthyFetcher 提供高级反机器人防护绕过能力,其中大部分在底层自动完成,其余部分由你显式开启。

正如 DynamicFetcher 一样,你需要了解 Playwright 的 Page API 才能对页面做自动化操作,这一点在 docs/fetching/dynamic.md 中有详细铺垫。官方文档列出的前置条件如下:

  1. 已阅读过 DynamicFetcher 页面,因为本类建立在它之上,本文档不重复那些内容;
  2. 已阅读过 Fetchers 基础,理解什么是 Response 对象,以及应该选择哪个 fetcher;
  3. 已阅读过 元素查询,理解如何从 Selector / Response 对象中查找/提取元素;
  4. 已阅读过 主类,知道 Response 类从 Selector 类继承了哪些属性和方法。

基本用法

所有 fetcher 共用唯一的导入方式:

from scrapling.fetchers import StealthyFetcher

解析器(parser)的通用配置方式见 docs/fetching/choosing.mdFetcher.configure(...)selector_config 参数)。

fetch 方法的异步版本即 async_fetch,这一点所有 fetcher 通用。

从源码看(scrapling/fetchers/stealth_chrome.py#L62-L63),StealthyFetcher.fetch 的实现极为精简:先把 selector_config 与全局解析器配置合并(并兼容旧参数名 custom_config),然后创建一个 StealthySession 上下文管理器并调用其 fetch。也就是说,单次 fetch 本质上是一次性的隐身会话,真正的抓取逻辑全部落在 StealthySession 上:

# scrapling/fetchers/stealth_chrome.py(节选)
with StealthySession(**kwargs) as engine:
    return engine.fetch(url)

它到底做了哪些隐身处理

StealthyFetcherDynamicFetcher 的隐身版本,官方文档列出的能力包括:

  1. 自动绕过所有类型的 Cloudflare Turnstile / Interstitial 验证;
  2. 绕过 CDP runtime 泄漏与 WebRTC 泄漏;
  3. 隔离 JS 执行、移除大量 Playwright 指纹、阻止通过某些机器人常见行为进行的检测;
  4. 生成 canvas 噪声,防止通过 canvas 进行指纹识别;
  5. 自动 patch 已知方法以检测 headless 模式运行,并提供选项对抗时区不匹配攻击;
  6. 以及其他反防护选项。

这些描述在源码中都有对应证据。当前版本(0.3.13 之后)底层引擎已从 Camoufox 替换为 patchrightscrapling/engines/_browsers/_stealth.py#L8-L9 直接 from patchright.sync_api import sync_playwright),默认启用其隔离执行上下文;文档特别提示:Stealthy 模式默认使用 Patchright 的隔离执行上下文,如果你的 page_action 需要读取 init_script 写到 window 上的全局变量,需从 action 中调用 page.evaluate(..., isolated_context=False)

会话初始化时,StealthySessionMixin.__validate__ 会为上下文预置一组"看起来像真实浏览器"的选项(scrapling/engines/_browsers/_base.py#L508-L519):

{
    "is_mobile": False,
    "has_touch": False,
    "service_workers": "allow",
    "ignore_https_errors": True,
    "screen": {"width": 1920, "height": 1080},
    "viewport": {"width": 1920, "height": 1080},
    "permissions": ["geolocation", "notifications"],
}

随后 __generate_stealth_optionsscrapling/engines/_browsers/_base.py#L522-L542)在未使用 cdp_url 时追加 DEFAULT_ARGS + STEALTH_ARGS 启动标志,并按开关追加:

参数 追加的浏览器标志
block_webrtc=True --webrtc-ip-handling-policy=disable_non_proxied_udp--force-webrtc-ip-handling-policy(确保策略强制执行)
allow_webgl=False --disable-webgl--disable-webgl-image-chromium--disable-webgl2
hide_canvas=True --fingerprinting-canvas-image-data-noise

这解释了文档参数表中 block_webrtc(强制 WebRTC 走代理、防止本地 IP 泄漏)、hide_canvas(canvas 随机噪声)与 allow_webgl 的实际落地方式——它们都是 Chromium 启动级开关,而非页面脚本注入。

完整参数列表

Scrapling 为这个 fetcher 及其会话类提供了大量选项。官方文档给出的完整参数表如下(其中 Optional 列中 ❌ 表示必填、✔️ 表示可选):

参数 说明 可选
url 目标 url
headless True 以 headless/隐藏模式运行浏览器(默认),False 为 headful/可见模式 ✔️
disable_resources 丢弃不必要的资源请求以提升速度。被丢弃的请求类型为 fontimagemediabeaconobjectimagesettexttrackwebsocketcsp_reportstylesheet ✔️
cookies 为下一个请求设置 cookies ✔️
useragent 传入要使用的 useragent 字符串。否则 fetcher 会生成并使用同浏览器同版本的真实 Useragent ✔️
network_idle 等待页面直到至少 500 ms 内没有网络连接 ✔️
load_dom 默认启用,等待页面上所有 JavaScript 完全加载并执行(等待 domcontentloaded 状态) ✔️
timeout 页面所有操作与等待使用的超时时间(毫秒)。默认 30,000 ms(30 秒) ✔️
wait 所有操作完成后、关闭页面并返回 Response 对象前等待的时间(毫秒) ✔️
page_action 用于自动化。传入一个接收 page 对象的函数,在导航后运行并执行所需自动化 ✔️
page_setup 一个接收 page 对象的函数,在导航前运行。用于注册必须在页面加载前设置好的事件监听器或路由 ✔️
wait_selector 等待某个 CSS 选择器进入特定状态 ✔️
init_script 一个 JavaScript 文件的绝对路径,在本会话所有页面创建时执行 ✔️
wait_selector_state Scrapling 将等待 wait_selector 给定的选择器达到该状态。默认状态为 attached ✔️
google_search 默认启用,Scrapling 会设置一个 Google referer 头 ✔️
extra_headers 要添加到请求的额外头字典。若同时使用,google_search 设置的 referer 优先于这里设置的 referer ✔️
proxy 请求使用的代理。可以是字符串,或仅含 'server'、'username'、'password' 键的字典 ✔️
real_chrome 如果你设备上安装了 Chrome 浏览器,启用后 Fetcher 将启动并使用你的浏览器实例 ✔️
locale 指定用户区域,如 en-GBde-DE 等。影响 navigator.languageAccept-Language 请求头以及数字与日期格式化规则。默认系统区域 ✔️
timezone_id 更改浏览器时区。默认系统时区 ✔️
cdp_url 不启动新的浏览器实例,而是连接该 CDP URL,通过 CDP 控制真实浏览器 ✔️
user_data_dir User Data Directory 路径,存储浏览器会话数据(cookies、local storage 等)。默认创建临时目录。仅在会话类中生效 ✔️
extra_flags 启动时传给浏览器的额外标志列表 ✔️
solve_cloudflare 启用后,fetcher 会在返回响应前解决所有类型的 Cloudflare Turnstile / Interstitial 挑战 ✔️
block_webrtc 强制 WebRTC 遵守代理设置,防止本地 IP 地址泄漏 ✔️
hide_canvas 为 canvas 操作添加随机噪声以防指纹识别 ✔️
allow_webgl 默认启用。禁用后将完全禁用 WebGL 与 WebGL 2.0 支持。不建议禁用 WebGL,因为许多 WAF 现在会检查 WebGL 是否启用 ✔️
additional_args 传给 Playwright context 的额外设置,优先级高于 Scrapling 的设置 ✔️
selector_config 创建最终 Selector / Response 类时使用的自定义解析参数字典 ✔️
blocked_domains 要阻断请求的目标域名集合。子域名同样匹配(例如 "example.com" 也会阻断 "sub.example.com" ✔️
block_ads 阻断约 3,500 个已知广告/跟踪域名。可与 blocked_domains 组合 ✔️
dns_over_https 通过 Cloudflare 的 DNS-over-HTTPS 路由 DNS 查询,防止使用代理时的 DNS 泄漏 ✔️
proxy_rotator 用于自动代理轮换的 ProxyRotator 实例。不能与 proxy 同时使用 ✔️
retries 失败请求的重试次数。默认 3 ✔️
retry_delay 两次重试之间等待的秒数。默认 1 ✔️
capture_xhr 传入一个正则 URL 模式字符串,捕获页面加载期间匹配该模式的 XHR/fetch 请求。捕获的响应可通过 response.captured_xhr 访问。默认 None(禁用) ✔️
executable_path 自定义浏览器可执行文件的绝对路径,替代内置的 Chromium。适用于非标准安装或自定义浏览器构建 ✔️

在会话类中,所有这些参数都可以设置为会话级全局配置;同时仍可在每次请求中单独传入部分可在"浏览器标签页级别"配置参数,例如:google_searchtimeoutwaitpage_actionpage_setupextra_headersdisable_resourceswait_selectorwait_selector_statenetwork_idleload_domsolve_cloudflareblocked_domainsproxyselector_config

这种"会话默认 + 单请求覆盖"的合并逻辑在 scrapling/engines/_browsers/_validators.py#L178-L215validate_fetch 函数中实现:先用会话 _config 中的值填充 _fetch_params 各字段,再把本次请求显式传入的覆盖项做 msgspec 校验后更新回去。

文档给出的 5 条重要备注(原文继承):

  1. 基本上与 DynamicFetcher 类的参数相同,但额外多了:solve_cloudflareblock_webrtchide_canvasallow_webgl 四个参数。capture_xhr 参数与 DynamicFetcher 共享。这一点可以从类型定义印证:StealthSession 就是 PlaywrightSession 加上这四个字段(scrapling/engines/_browsers/_types.py#L117-L121)。
  2. disable_resources 选项在作者的测试中让部分网站的请求快了约 25%,并能节省代理用量,但需谨慎,它可能导致某些网站永远加载不完。
  3. google_search 参数对所有请求默认启用,将 referer 设为 https://www.google.com/。与 extra_headers 一起使用时,其 referer 优先级更高。源码中该逻辑位于 scrapling/engines/_browsers/_stealth.py#L212-L215:仅当 extra_headers 未显式包含 referer 键时才注入 Google referer。
  4. 如果你未设置 user agent 且启用了 headless 模式,fetcher 会为相同浏览器版本生成一个真实 user agent 并使用;如果你未设置 user agent 且未启用 headless 模式,fetcher 将使用浏览器默认 user agent(在最新版本中与标准浏览器一致)。
  5. init_script 注册在浏览器 context 上,因此在页面创建时运行。Stealthy 模式默认使用 Patchright 的隔离执行上下文;如果你的 page_action 需要读取脚本放到 window 上的全局变量,请从 action 中调用 page.evaluate(..., isolated_context=False)

此外,StealthConfigscrapling/engines/_browsers/_validators.py#L144-L155)还有一个文档未明示的自动行为:开启 solve_cloudflaretimeout 小于 60,000 ms 时,超时会被自动提升到 60,000 ms——这与文档"使用 Cloudflare 求解器时超时应至少 60 秒"的建议相互印证。

实战示例

Cloudflare 与隐身选项

# 自动 Cloudflare 求解器
page = StealthyFetcher.fetch('https://nopecha.com/demo/cloudflare', solve_cloudflare=True)

# 与其他隐身选项组合
page = StealthyFetcher.fetch(
    'https://protected-site.com',
    solve_cloudflare=True,
    block_webrtc=True,
    real_chrome=True,
    hide_canvas=True,
    google_search=True,
    proxy='http://username:password@host:port',  # 也可以是仅含 'server'、'username'、'password' 键的字典
)

solve_cloudflare 参数启用对 Cloudflare Turnstile / Interstitial 挑战的自动检测与求解,覆盖:

  • JavaScript 挑战(managed)
  • 交互式挑战(点击验证框)
  • 隐形挑战(自动后台验证)

甚至可以解决内嵌了验证码的自定义页面(embedded 场景)。

重要说明:

  1. 对于使用自定义实现的网站,有时你需要用 wait_selector 确保 Scrapling 在解决验证码后等待真实网站内容加载完成。部分网站是"边缘案例",而求解器正尽可能保持通用。
  2. 使用 Cloudflare 求解器时,超时应至少 60 秒,以留出充足的挑战求解时间。
  3. 该功能可与代理及其他隐身选项无缝配合。

从源码看,求解入口在 fetch 流程中(scrapling/engines/_browsers/_stealth.py#L253-L256):先 _wait_for_page_stabilityload/domcontentloaded/networkidle 三级等待,见 _base.py#L142-L147),然后调用 self._cloudflare_solver(page),求解完成后再做一次页面稳定性等待,随后才轮到 page_actionwait_selector

浏览器自动化

这里就是你的 Playwright Page API 知识发挥作用的地方。你传入的函数接收 Playwright API 的 page 对象,执行期望的操作后 fetcher 继续流程。

该函数在等待 network_idle(如果启用)之后、等待 wait_selector 参数之前立即执行,因此它可用于自动化之外的目的——你可以任意修改页面。

下面的示例使用页面的 mouse events 以滚轮方式滚动页面,然后移动鼠标:

from playwright.sync_api import Page

def scroll_page(page: Page):
    page.mouse.wheel(10, 0)
    page.mouse.move(100, 400)
    page.mouse.up()

page = StealthyFetcher.fetch('https://example.com', page_action=scroll_page)

如果你使用异步 fetch 版本,函数也必须是异步的:

from playwright.async_api import Page

async def scroll_page(page: Page):
    await page.mouse.wheel(10, 0)
    await page.mouse.move(100, 400)
    await page.mouse.up()

page = await StealthyFetcher.async_fetch('https://example.com', page_action=scroll_page)

在源码中,page_setuppage.goto 之前执行(scrapling/engines/_browsers/_stealth.py#L240-L247),page_action 在导航与稳定性等待之后执行;两者抛出的异常都会被捕获并记录日志,不会中断整个抓取流程。

等待条件

# 等待某个选择器
page = StealthyFetcher.fetch(
    'https://quotes.toscrape.com/js-delayed/',
    wait_selector='.quote',
    wait_selector_state='visible'
)

这是 fetcher 在返回响应前执行的最后一次等待(如果启用)。你向 wait_selector 传入一个 CSS 选择器,fetcher 将等待 wait_selector_state 中传入的状态达成。如果不传状态,默认是 attached,即等待元素出现在 DOM 中。

之后,如果 load_dom 启用(默认),fetcher 会再检查所有 JavaScript 文件是否已加载并执行(domcontentloaded 状态),否则继续等待。如果你启用了 network_idle,fetcher 会再次等待 network_idle 达成。

可等待的状态(对应 Playwright 的 page.wait_for_selector 语义):

  • attached:等待元素出现在 DOM 中。
  • detached:等待元素不在 DOM 中。
  • visible:等待元素具有非空包围盒且没有 visibility:hidden。注意没有内容或带 display:none 的元素包围盒为空,不视为可见。
  • hidden:等待元素从 DOM 中分离、或包围盒为空、或 visibility:hidden。与 visible 选项相反。

真实场景示例(Amazon)

以下示例仅供教育目的;该示例由 AI 生成,也展示了通过 AI 使用 Scrapling 有多简单:

def scrape_amazon_product(url):
    # 使用 StealthyFetcher 绕过防护
    page = StealthyFetcher.fetch(url)

    # 提取产品详情
    return {
        'title': page.css('#productTitle::text').get().clean(),
        'price': page.css('.a-price .a-offscreen::text').get(),
        'rating': page.css('[data-feature-name="averageCustomerReviews"] .a-popover-trigger .a-color-base::text').get(),
        'reviews_count': page.css('#acrCustomerReviewText::text').re_first(r'[\d,]+'),
        'features': [
            li.get().clean() for li in page.css('#feature-bullets li span::text')
        ],
        'availability': page.css('#availability')[0].get_all_text(strip=True),
        'images': [
            img.attrib['src'] for img in page.css('#altImages img')
        ]
    }

会话管理:StealthySession / AsyncStealthySession

为了在相同配置下发起多个请求时保持浏览器打开,使用 StealthySession / AsyncStealthySession 类。这些类可以接收 fetch 函数能接受的所有参数,让你为整个会话指定一份配置。

from scrapling.fetchers import StealthySession

# 使用默认配置创建会话
with StealthySession(
    headless=True,
    real_chrome=True,
    block_webrtc=True,
    solve_cloudflare=True
) as session:
    # 使用同一浏览器实例发起多个请求
    page1 = session.fetch('https://example1.com')
    page2 = session.fetch('https://example2.com')
    page3 = session.fetch('https://nopecha.com/demo/cloudflare')

    # 所有请求复用同一浏览器实例上的同一个标签页

异步会话用法

import asyncio
from scrapling.fetchers import AsyncStealthySession

async def scrape_multiple_sites():
    async with AsyncStealthySession(
        real_chrome=True,
        block_webrtc=True,
        solve_cloudflare=True,
        timeout=60000,  # 60 秒,用于 Cloudflare 挑战
        max_pages=3
    ) as session:
        # 使用共享浏览器配置发起异步请求
        pages = await asyncio.gather(
            session.fetch('https://site1.com'),
            session.fetch('https://site2.com'),
            session.fetch('https://protected-site.com')
        )
        return pages

你可能注意到了 max_pages 参数。这是一个新参数,它让 fetcher 创建轮转的浏览器标签页池(rotating pool of Browser tabs)。不再是所有请求都用一个标签页,而是设置同时可显示的标签页数量上限。每个请求到来时,库会关闭所有已完成任务的标签页,并检查当前标签页数量是否低于最大允许页数/标签页数,然后:

  1. 如果还在允许范围内,fetcher 会为你创建一个新标签页,之后一切照常。
  2. 否则,它会以亚秒级间隔持续检查 60 秒,看是否允许创建新标签页,然后抛出 TimeoutError。当抓取的目标网站无响应时可能发生这种情况。

这套逻辑允许同一个浏览器内同时抓取多个 URL,节省大量资源,而且速度极快。在 0.3 和 0.3.1 版本中,该池曾复用已完成的标签页以节省更多资源/时间,但该逻辑被证明有缺陷,因为几乎不可能让页面/标签页免于被上一次请求的配置"污染"。从源码看,60 秒上限对应 _max_wait_for_page = 60scrapling/engines/_browsers/_base.py#L227),标签页生命周期由 PagePool_page_generator 上下文管理器管理(scrapling/engines/_browsers/_base.py#L182-L215)。

会话的收益

  • 浏览器复用:复用同一浏览器实例,后续请求快得多。
  • Cookie 持久化:像普通浏览器一样自动处理 cookie 与会话状态。
  • 一致的指纹:所有请求共享同一浏览器指纹。
  • 内存效率:相比每次 fetch 都启动新浏览器,资源利用更好。

从源码结构看,普通模式(未配置 proxy_rotator 且未指定 cdp_url)下,start() 使用 launch_persistent_context 启动持久化 context 并合并浏览器选项、context 选项与 user_data_dirscrapling/engines/_browsers/_stealth.py#L90-L93);这正是"会话级 cookie/本地存储持久化"的实现基础。

Cloudflare 求解器内部机制(源码级)

solve_cloudflare 的核心是 _cloudflare_solver_detect_cloudflare 两个方法。

挑战类型检测scrapling/engines/_browsers/_base.py#L544-L577):静态方法 _detect_cloudflare 在页面 HTML 中查找 cType: 'non-interactive'cType: 'managed'cType: 'interactive' 三种标记;若都不匹配,再用 Selector 检查页面中是否存在 script[src*="challenges.cloudflare.com/turnstile/v"],有则判定为 embedded(内嵌式 Turnstile)。测试用例 tests/fetchers/sync/test_stealth_session.py#L72-L93 对这三类检测与"普通页面返回 None"都做了断言。

求解流程scrapling/engines/_browsers/_stealth.py#L107-L182):

  1. 先等待 5 秒内的 networkidle,然后检测挑战类型;未检测到则记录 "No Cloudflare challenge found." 并直接返回。
  2. non-interactive(隐形)类型:只要页面标题仍是 <title>Just a moment...</title> 就每秒检查一次,直到等待页消失。
  3. 交互式/托管类型:定位 Cloudflare 挑战 iframe——URL 匹配模块级常量 __CF_PATTERN__(正则 ^https?://challenges\.cloudflare\.com/cdn-cgi/challenge-platform/.*,见 _stealth.py#L19);若找不到 iframe,回退到 #cf_turnstile div, #cf-turnstile div, .turnstile>div>div(embedded 场景)或 .main-content p+div>div>div 选择器定位验证框。
  4. 取验证框的 bounding box 后,在固定随机偏移处模拟点击:captcha_x, captcha_y = outer_box["x"] + randint(26, 28), outer_box["y"] + randint(25, 27),并以 delay=randint(100, 200) 的鼠标点击执行,模拟真实用户而非毫秒级精确点击。
  5. 点击后再次 _wait_for_page_stability;若 "Just a moment..." 标题仍在,则以递归方式重新求解(return self._cloudflare_solver(page))。

重试、代理与请求流程(源码级)

StealthySession.fetch 的完整流程(scrapling/engines/_browsers/_stealth.py#L184-L300)值得完整理解,因为它串起了多个参数的实际语义:

  1. 代理解析:先 kwargs.pop("proxy") 取出单请求静态代理;若会话配置了 proxy_rotator 且未传静态代理,则调用 proxy_rotator.get_proxy() 轮换取代理,否则使用静态代理。
  2. Google referer 注入google_search 为真且 extra_headers 中未显式包含 referer 键时,注入 https://www.google.com/ 作为 page.goto 的 referer。
  3. 重试循环for attempt in range(self._config.retries)(默认 3 次)。每次通过 _page_generator 获取标签页(使用代理时为该代理创建全新 context,用完即关,避免配置污染)。
  4. 响应捕获:通过 page.on("response", ...) 注册处理器,记录主框架的导航 document 响应;若配置了 capture_xhr,还会把匹配正则的 xhr/fetch 类型响应收进列表,最终挂到 response.captured_xhr(逻辑见 scrapling/engines/_browsers/_base.py#L149-L180)。
  5. 执行顺序page_setup(导航前)→ page.goto(url, referer=referer)_wait_for_page_stabilityload + 可选 domcontentloaded + 可选 networkidle)→ 可选 _cloudflare_solver + 再次稳定性等待 → 可选 page_action → 可选 wait_selectorlocator.first.wait_for(state=...))+ 稳定性等待 → page.wait_for_timeout(params.wait)ResponseFactory.from_playwright_response(...) 返回 Response 对象(meta 中记录本次使用的代理)。
  6. 失败处理:捕获异常后 page_info.mark_error(),若还有剩余次数,区分代理错误(is_proxy_error)与普通错误打印不同日志,等待 retry_delay(默认 1 秒)后重试;最后一次失败则抛出异常。

这一链路也解释了文档备注中"会话中所有参数可全局设置、部分参数可按请求覆盖"的原因:_fetch_paramsscrapling/engines/_browsers/_validators.py#L158-L175)只包含可覆盖字段(google_searchtimeoutwaitpage_actionpage_setupextra_headersdisable_resourceswait_selectorwait_selector_statenetwork_idleload_domblocked_domainssolve_cloudflareselector_config),与文档列出的可覆盖清单一致。

使用 Camoufox 作为引擎

0.3.13 版本之前,这个 fetcher 使用 Camoufox 的定制版作为引擎,之后因诸多原因被 patchright 取代。如果你确认 Camoufox 在你的设备上稳定、没有严重的内存问题、并且希望继续使用,完全可以。

首先,如果尚未安装 Camoufox 库、浏览器与 Firefox 系统依赖,需要安装:

pip install camoufox
playwright install-deps firefox
camoufox fetch

然后继承 StealthySession 并重写 start

from scrapling.fetchers import StealthySession
from playwright.sync_api import sync_playwright
from camoufox.utils import launch_options as generate_launch_options

class StealthySession(StealthySession):
    def start(self):
        """Create a browser for this instance and context."""
        if not self.playwright:
            self.playwright = sync_playwright().start()
            # 在这里配置 camoufox 运行选项
            launch_options = generate_launch_options(**{"headless": True, "user_data_dir": ''})
            # 示例,部分内容是 0.3.13 之前我们一直在做的
            launch_options = generate_launch_options(**{
                "geoip": False,
                "proxy": self._config.proxy,
                "headless": self._config.headless,
                "humanize": True if self._config.solve_cloudflare else False,  # 针对 Cloudflare 建议启用 humanize,其余看你需求
                "i_know_what_im_doing": True,  # 使用自定义用户配置时关闭警告
                "allow_webgl": self._config.allow_webgl,
                "block_webrtc": self._config.block_webrtc,
                "os": None,
                "user_data_dir": self._config.user_data_dir,
                "firefox_user_prefs": {
                    # 这就是 `enable_cache` 内部做的事,所以改由这里来做
                    "browser.sessionhistory.max_entries": 10,
                    "browser.sessionhistory.max_total_viewers": -1,
                    "browser.cache.memory.enable": True,
                    "browser.cache.disk_cache_ssl": True,
                    "browser.cache.disk.smart_size.enabled": True,
                },
                # 等等……
            })
            self.context = self.playwright.firefox.launch_persistent_context(**launch_options)
        else:
            raise RuntimeError("Session has been already started")

之后即可像以前一样正常使用,甚至包括求解 Cloudflare 挑战:

with StealthySession(solve_cloudflare=True, headless=True) as session:
    page = session.fetch('https://sergiodemo.com/security/challenge/legacy-challenge')
    if page.css('#page-not-found-404'):
        print('Cloudflare challenge solved successfully!')

同一套逻辑适用于 AsyncStealthySession,只有少量差异(start 变为 async def,使用 async_playwrightawait):

from scrapling.fetchers import AsyncStealthySession
from playwright.async_api import async_playwright
from camoufox.utils import launch_options as generate_launch_options

class AsyncStealthySession(AsyncStealthySession):
    async def start(self):
        """Create a browser for this instance and context."""
        if not self.playwright:
            self.playwright = await async_playwright().start()
            # 在这里配置 camoufox 运行选项
            launch_options = generate_launch_options(**{"headless": True, "user_data_dir": ''})
            # 或按上面示例设置启动选项
            self.context = await self.playwright.firefox.launch_persistent_context(**launch_options)
        else:
            raise RuntimeError("Session has been already started")

async with AsyncStealthySession(solve_cloudflare=True, headless=True) as session:
    page = await session.fetch('https://sergiodemo.com/security/challenge/legacy-challenge')
    if page.css('#page-not-found-404'):
        print('Cloudflare challenge solved successfully!')

什么时候该用 StealthyFetcher

官方文档给出的适用场景清单(When to Use):

  • 需要绕过反机器人防护(bypassing anti-bot protection)
  • 需要可靠的浏览器指纹(reliable browser fingerprint)
  • 需要完整的 JavaScript 支持(full JavaScript support)
  • 希望自动化的隐身特性(automatic stealth features)
  • 需要浏览器自动化(browser automation)
  • 正在处理 Cloudflare 防护(Cloudflare protection)

docs/fetching/choosing.md 的对比表中,StealthyFetcherDynamicFetcher 同速(🐇🐇🐇)、同内存等级,但隐身与反机器人选项为最高档(⭐⭐⭐⭐⭐),最适合"动态加载网站 + 小型自动化 + 复杂防护"的组合。若你的目标只是无防护的动态页面,可先评估更轻量的 DynamicFetcherdocs/fetching/dynamic.md)或纯 HTTP 的 Fetcher

延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.79 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
390