首页
/ Scrapling StealthyFetcher 实战指南:绕过 Cloudflare 与反爬指纹检测的隐身浏览器抓取

Scrapling StealthyFetcher 实战指南:绕过 Cloudflare 与反爬指纹检测的隐身浏览器抓取

2026-09-03 17:52:50作者:宣聪麟

StealthyFetcher 是 Scrapling 提供的隐身浏览器抓取器,基于 Chromium + Patchright 构建,自动处理绝大多数反机器人检测:Cloudflare Turnstile/Interstitial 挑战、CDP 运行时泄漏、WebRTC 本地 IP 泄漏、Canvas 指纹等。读完本文,你将掌握 StealthyFetcher 的完整参数体系、Cloudflare 自动求解机制、页面自动化与等待条件控制,以及 StealthySession/AsyncStealthySession 会话复用与标签页池的原理,并能直接写出可运行的抓取代码。

基本用法与定位

StealthyFetcher 是与 DynamicFetcher 同源的隐身浏览器抓取器,共享同一套浏览器自动化模型,页面交互基于 Playwright 的 Page API。与 DynamicFetcher 的区别在于:自 0.3.13 版本起,原 DynamicFetcher 上的 stealth 选项被整体迁入 StealthyFetcher,并新增了 solve_cloudflareblock_webrtchide_canvasallow_webgl 四个专属参数。所有 Fetcher 的导入方式统一:

from scrapling.fetchers import StealthyFetcher

解析选项(parser 配置)的说明见 Fetcher 选择指南。异步版本的 fetch 方法名为 async_fetch

StealthyFetcher 会自动完成以下隐身工作:

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

从源码结构看,这套能力建立在 Patchright(Playwright 的隐身补丁版)之上而非原生 Playwright:_stealth.py 直接 from patchright.sync_api import sync_playwright / from patchright.async_api import async_playwright,而 DynamicFetcher 使用的是普通 Playwright。

完整参数列表

StealthyFetcher 与其会话类提供大量可配置项,完整列表如下:

参数 说明 可选
url 目标 URL
headless True 以 headless/隐藏模式运行浏览器(默认),False 为 headful/可见模式。
disable_resources 丢弃不必要的资源请求以提速。被丢弃的类型有 fontimagemediabeaconobjectimagesettexttrackwebsocketcsp_reportstylesheet
cookies 为下一次请求设置 cookies。
useragent 传入要使用的 User-Agent 字符串。否则 fetcher 会生成并使用同浏览器、同版本的真实 User-Agent。
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 等待 wait_selector 给定的选择器满足指定状态。默认状态为 attached
google_search 默认启用,Scrapling 会设置 Google referer 请求头。
extra_headers 为请求添加的额外请求头字典。若与 google_search 一起使用,google_search 设置的 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 和本地存储等浏览器会话数据。默认为临时目录。仅对会话类生效
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)可以在每次请求时单独覆盖,即这些是"标签页级别"可配置的参数。

注意事项:

  1. 参数基本上与 DynamicFetcher 相同,只是多了 solve_cloudflareblock_webrtchide_canvasallow_webgl 这几个参数;
  2. disable_resources 在测试中使某些网站的请求快了约 25%,还能节省代理流量,但需谨慎使用——它可能导致某些网站永远无法完成加载;
  3. google_search 默认对所有请求启用,将 referer 设为 https://www.google.com/。若与 extra_headers 同时使用,其 referer 优先;
  4. 如果未设置 user agent 且启用了 headless 模式,fetcher 会生成同浏览器版本的真实 user agent 并使用;如果未设置 user agent 且未启用 headless 模式,则使用浏览器默认 user agent(最新版本的浏览器中与普通浏览器一致);
  5. init_script 注册在浏览器 context 上,因此页面创建时就会执行。Stealthy 模式默认使用 Patchright 的隔离执行上下文;如果你的 page_action 需要读取脚本放到 window 上的全局变量,请在 action 中调用 page.evaluate(..., isolated_context=False)

参数背后的底层实现

从源码可以印证这些隐身参数的实际作用机制。StealthySessionMixin.__generate_stealth_options 中:

  • block_webrtc=True 会追加启动参数 --webrtc-ip-handling-policy=disable_non_proxied_udp--force-webrtc-ip-handling-policy,强制 WebRTC 走代理,防止本地 IP 从 UDP 信道泄漏;
  • allow_webgl=False 会追加 --disable-webgl--disable-webgl-image-chromium--disable-webgl2
  • hide_canvas=True 会追加 --fingerprinting-canvas-image-data-noise,由浏览器内核对 Canvas 图像数据注入噪声。

这些标志仅在未使用 cdp_url 时生效(连接外部浏览器时无意义)。此外 会话初始化 还固定了 1920x1080 的屏幕/视口、关闭 is_mobile/has_touch、允许 Service Worker、忽略 HTTPS 错误并授予地理位置与通知权限——这些都是让指纹"看起来像真人桌面浏览器"的默认基线。

StealthyFetcher.fetch() 本身只是一个薄封装:stealth_chrome.py 中它合并 selector_configwith StealthySession(**kwargs) as engine: return engine.fetch(url),即每次 fetch 调用都会创建一个临时会话上下文,用完即关;async_fetch 同理包装 AsyncStealthySession。导出入口见 fetchers/__init__.py 的惰性导入定义。

示例

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)
  • 交互式挑战(点击验证框)
  • 无感挑战(后台自动验证)

甚至能求解内嵌 captcha 的自定义页面。

重要提示:

  1. 对于使用自定义实现的网站,你可能需要用 wait_selector 确保 Scrapling 在求解 captcha 后等待真实网站内容加载完成。有些网站是真正的边缘案例,因为求解器要尽可能通用;
  2. 使用 Cloudflare 求解器时,超时应至少设置为 60 秒,给足挑战求解时间;
  3. 该功能可与代理及其他隐身选项无缝配合。

求解器的源码级工作流

solve_cloudflare=True 时,实际执行流程在 _cloudflare_solver(同步版,异步版在 L382-L457)中:

  1. 检测挑战类型_detect_cloudflare 在页面内容中匹配 cType: 'non-interactive'cType: 'managed'cType: 'interactive' 三种 Turnstile 标记;若都未命中,则用 CSS 选择器 script[src*="challenges.cloudflare.com/turnstile/v"] 判断是否为 embedded(内嵌式)挑战。测试用例 test_stealth_session.py 逐一验证了这四种类型的识别结果;
  2. 无感挑战:只要页面标题还是 Just a moment...,就每秒轮询一次并等待 load 状态,直到挑战页消失;
  3. 交互/托管/内嵌挑战:先通过 URL 正则 __CF_PATTERN__(匹配 challenges.cloudflare.com/cdn-cgi/challenge-platform/...)定位挑战 iframe,取 iframe 元素的 bounding_box();找不到 iframe 时回退到页面内的 box 选择器(#cf_turnstile div, #cf-turnstile div, .turnstile>div>div.main-content p+div>div>div)。随后在验证框坐标上叠加 randint(26, 28) 像素的随机偏移模拟人类点击(page.mouse.click(..., delay=randint(100, 200))),点击后等待网络空闲并轮询至多 10 秒确认 Just a moment... 标题消失;若仍在,则递归重试求解。正则匹配边界由 test_cf_pattern_regex 测试覆盖。

在 fetch 主流程中(fetch 方法),Cloudflare 求解发生在 page.goto 与首次 _wait_for_page_stability 之后、page_action 之前;求解完成后还会再执行一次 _wait_for_page_stability(page, load_dom, network_idle) 确保 captcha 之后页面完全加载——这解释了为什么"求解后建议配合 wait_selector"。

浏览器自动化

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

该函数在 network_idle 等待(如果启用)结束后、wait_selector 参数等待之前执行,因此也可用于自动化之外的目的——你可以随意修改页面。下面的例子使用了页面的鼠标事件,用鼠标滚轮滚动页面,然后移动鼠标:

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_action 的时机差异:page_setuppage.goto 之前执行,适合注册 page.route 或事件监听;两者在源码中均被 try/except 包裹,抛出的异常会被记录日志而不中断抓取流程。

等待条件

# 等待选择器
page = StealthyFetcher.fetch(
    'https://example.com',
    wait_selector='h1',
    wait_selector_state='visible'
)

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

之后,如果 load_dom 启用(默认),fetcher 会再检查一次所有 JS 是否已加载并执行(domcontentloaded 状态),否则继续等待。如果启用了 network_idle,fetcher 会再次等待 network_idle 满足。

可等待的状态包括以下四种:

  • 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 类。它们可以接受 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,  # Cloudflare 挑战需要 60 秒
        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 能够创建轮换的浏览器标签页池。不再所有请求共用一个标签页,而是限制同时打开的最大页面数。每次请求时,库会关闭所有已完成任务的标签页,并检查当前标签页数是否低于上限,然后:

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

这套逻辑允许在同一个浏览器中同时抓取多个 URL,节省大量资源,而且速度很快。在 0.3 和 0.3.1 版本中,标签页池曾复用已完成的标签页以进一步节省资源/时间,但该逻辑被证明有缺陷——几乎不可能保护页面/标签页不被上一个请求的配置污染。

会话默认使用持久化 context:start 方法 中,非 CDP 场景下调用 launch_persistent_context 并指定 user_data_dir(默认为临时目录),这就是"会话间 cookie 自动保持"的机制来源;而 cdp_urlproxy_rotator 场景则分别走 connect_over_cdpchromium.launch

会话的收益

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

重试与代理细节

fetch 主循环 可以确认几个文档未展开的默认行为:

  • 每个请求在 retries(默认 3)次尝试内循环执行,失败后等待 retry_delay(默认 1 秒)再重试;若是代理类错误(is_proxy_error 判定),日志会明确标注是哪个代理失败;
  • 会话配置了 proxy_rotator 时,每次尝试都会从轮换器取一个新代理;而请求级传入的静态 proxy 会覆盖轮换器与 session 代理,并为其单独创建浏览器 context;
  • 会话已关闭后调用 fetch 会抛出 RuntimeError("Context manager has been closed")

何时使用 StealthyFetcher

在以下场景应使用 StealthyFetcher

  • 需要绕过反机器人保护;
  • 需要可靠的浏览器指纹;
  • 需要完整的 JavaScript 支持;
  • 希望自动获得隐身能力;
  • 需要浏览器自动化;
  • 目标站使用 Cloudflare 保护。

相关文档与延伸阅读

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

项目优选

收起
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