使用 StealthyFetcher 在浏览器中获取页面¶
StealthyFetcher 可以加载 JavaScript 页面,并通过带有反机器人应对功能的 Chromium 或 Google Chrome 执行自动化。它使用经过隐身调整的 Playwright API。
要自动操作页面,需要了解一些 Playwright Page API,后面会说明。
前提条件
- 已阅读 Fetcher 基础,了解 Response 对象,并知道如何选择 Fetcher。
- 已阅读查询元素,了解如何从 Selector/Response 对象中查找和提取元素。
- 已阅读主要类,了解 Response 从 Selector 继承的属性和方法。
基本用法¶
这个 Fetcher 的主要导入方式与其他 Fetcher 相同:
解析选项的配置方法见这里。下面通过示例逐个介绍大部分参数。如需快速查阅,可以直接查看完整参数表。
说明
fetch 的异步版本是 async_fetch。
此 Fetcher 提供两种本地浏览器运行方式。
分别是:
1. 自带的 Chromium¶
打开自带的 Chromium,使用默认隐身设置并加载页面。2. 已安装的 Chrome¶
如果设备上已安装 Google Chrome,可以使用这个选项。它与第一种方式类似,但使用本机的 Google Chrome 而不是 Chromium。这样请求更接近真实用户,也更难被识别为自动化。如果还没有安装 Google Chrome,可以通过以下终端命令为库安装,而不必手动处理:
远程会话¶
使用 RemoteSession 或 AsyncRemoteSession,通过 Chrome DevTools Protocol 连接已运行的浏览器。将 WebSocket 地址(ws:///wss://)或 HTTP 地址(如 http://localhost:9222)传给 cdp_url。这些类仅用于会话。StealthySession 和 StealthyFetcher 不接受 cdp_url。
这两个类分别继承自 StealthySession 和 AsyncStealthySession,因此所有选项和功能都可照常使用,包括通过 solve_cloudflare=True 解决 Cloudflare 验证,但下文列出的浏览器启动和上下文限制除外。
from scrapling.fetchers import RemoteSession
with RemoteSession(cdp_url="http://localhost:9222", new_context=False) as session:
page = session.fetch("https://example.com")
new_context=True 是默认值。它会创建独立的上下文,应用上下文选项,并在会话结束时关闭该上下文。
new_context=False 使用现有的默认上下文及其登录状态。会话结束后,该上下文保持打开。如果没有可用的上下文,启动会话时会报错。
复用上下文时,会忽略 useragent、locale、timezone_id、proxy、proxy_rotator 和 additional_args,也会忽略每次请求的 proxy。继续使用上下文的现有设置。
两种模式下,Scrapling 都会创建自己的标签页,并仅关闭这些标签页。原有标签页保持打开。extra_headers、timeout、page_setup 和 page_action 等页面选项仍然有效。
仍可添加 cookies 和 init_script,但在 new_context=False 时,它们会影响共享上下文。远程会话类不接受 real_chrome;浏览器由 cdp_url 指定。浏览器已在运行,因此其他浏览器启动选项不生效。
通过 async with 和 await 使用 AsyncRemoteSession:
from scrapling.fetchers import AsyncRemoteSession
async def scrape_remote():
async with AsyncRemoteSession(cdp_url="http://localhost:9222", new_context=False) as session:
return await session.fetch("https://example.com")
它提供哪些功能?¶
StealthyFetcher 提供以下反机器人应对功能:
- 通过
solve_cloudflare=True自动处理所有类型的 Cloudflare Turnstile 和 Interstitial 挑战。 - 避免 CDP runtime 和 WebRTC 泄露信息。
- 隔离 JavaScript 执行,移除多种 Playwright 指纹,并避免一些已知机器人行为触发检测。
- 为 Canvas 添加随机噪声,防止通过 Canvas 识别指纹。
- 自动处理已知的无头模式检测方式,并提供选项来应对时区不一致检测。
- 还有其他应对防护的选项。
完整参数列表¶
这个 Fetcher 及其会话类提供很多选项。在查看示例前,先列出完整参数:
| 参数 | 说明 | 可选 |
|---|---|---|
| url | 目标 URL。 | ❌ |
| headless | True 表示无头模式,即不显示窗口,默认如此。False 表示显示浏览器窗口。 |
✔️ |
| disable_resources | 阻止不必要的资源请求以提高速度。类型包括 font、image、media、beacon、object、imageset、texttrack、websocket、csp_report 和 stylesheet。 |
✔️ |
| cookies | 为下一次请求设置 Cookie。 | ✔️ |
| useragent | 指定 User-Agent 字符串。未指定时,Fetcher 会生成并使用相同浏览器及版本的真实 User-Agent。 | ✔️ |
| network_idle | 等待页面至少连续 500 ms 没有网络连接。 | ✔️ |
| load_dom | 等待页面上的所有 JavaScript 加载并执行,即等待 domcontentloaded 状态。默认开启。 |
✔️ |
| pierce_shadow | 将开放的 Shadow DOM 内容包含在响应 HTML 中,默认为 False。可以为整个会话设置,也可以按请求覆盖。 |
✔️ |
| 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 | 设置 Google referer 请求头,默认开启。 | ✔️ |
| extra_headers | 要添加到请求中的额外请求头字典。同时使用时,google_search 设置的 referer 优先于这里的值。 |
✔️ |
| proxy | 请求使用的代理,可以是字符串,或仅包含 'server'、'username'、'password' 的字典。 | ✔️ |
| real_chrome | 启用后,Fetcher 会启动并使用设备上已安装的 Chrome。 | ✔️ |
| locale | 区域设置,例如 en-GB、de-DE。影响 navigator.language、请求头 Accept-Language,以及数字和日期格式。默认使用系统区域设置。 |
✔️ |
| timezone_id | 修改浏览器时区,默认使用系统时区。 | ✔️ |
| user_data_dir | 用户数据目录的路径,用于保存 Cookie、本地存储等浏览器会话数据。默认创建临时目录。仅支持会话。 | ✔️ |
| extra_flags | 启动浏览器时传入的额外参数列表。 | ✔️ |
| solve_cloudflare | 返回响应前,处理所有类型的 Cloudflare Turnstile 和 Interstitial 挑战。默认为 False。 |
✔️ |
| block_webrtc | 强制 WebRTC 遵守代理设置,避免泄露本地 IP 地址。 | ✔️ |
| hide_canvas | 为 Canvas 操作添加随机噪声,防止指纹识别。 | ✔️ |
| allow_webgl | 默认开启。关闭后会完全禁用 WebGL 和 WebGL 2.0。不建议关闭,因为很多 WAF 会检查 WebGL 是否启用。 | ✔️ |
| additional_args | 传给 Playwright 上下文的额外设置,优先级高于 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。 | ✔️ |
| record_requests | 在 session.network 中保留已完成的请求及保存的响应。仅用于会话,默认为 False。 |
✔️ |
| max_recorded_requests | 每个会话保留的最大请求条目数,默认为 1,000。保存的响应体还受固定大小限制。 | ✔️ |
| executable_path | 自定义浏览器可执行文件的绝对路径,用来替代自带的 Chromium。适用于特殊安装方式或自定义浏览器构建。 | ✔️ |
在会话类中,可以为整个会话设置这些参数。部分标签页级设置还可以在每次请求中单独覆盖,包括 google_search、timeout、wait、page_action、page_setup、extra_headers、disable_resources、wait_selector、wait_selector_state、network_idle、load_dom、pierce_shadow、solve_cloudflare、blocked_domains、proxy 和 selector_config。
注意事项:
- 在我的测试中,
disable_resources让部分网站的请求加快了约 25%,也能减少代理流量。但要谨慎使用,因为它可能导致某些网站一直无法完成加载。 - 所有请求默认启用
google_search,将 referer 设为https://www.google.com/。它的优先级高于extra_headers中设置的 referer。 - 未指定 User-Agent 且启用无头模式时,Fetcher 会生成并使用同版本浏览器的真实 User-Agent。未启用无头模式时,则使用浏览器默认的 User-Agent,在当前版本中与普通浏览器相同。
init_script注册在浏览器上下文中,因此会在页面创建时运行。隐身模式默认使用 Patchright 的隔离执行上下文。如果page_action需要读取脚本放在window上的全局值,应在操作函数中调用page.evaluate(..., isolated_context=False)。
会话管理¶
使用 StealthySession/AsyncStealthySession,可以在多次请求期间保持浏览器打开,并复用同一配置。这些类支持 fetch 的所有参数,因此可以统一设置整个会话。
from scrapling.fetchers import StealthySession
with StealthySession(
headless=True,
disable_resources=True,
real_chrome=True
) as session:
page1 = session.fetch('https://example1.com')
page2 = session.fetch('https://example2.com')
page3 = session.fetch('https://dynamic-site.com')
使用异步会话¶
import asyncio
from scrapling.fetchers import AsyncStealthySession
async def scrape_multiple_sites():
async with AsyncStealthySession(
network_idle=True,
timeout=30000,
max_pages=3
) as session:
pages = await asyncio.gather(
session.fetch('https://spa-app1.com'),
session.fetch('https://spa-app2.com'),
session.fetch('https://dynamic-content.com')
)
return pages
max_pages 控制浏览器标签页池中同时打开的最大标签页数。请求完成后,标签页保持打开。每次请求时,库会按以下步骤处理:
- 如果有空闲标签页,就复用它。每次请求都会应用自己的
timeout、extra_headers、disable_resources、blocked_domains等标签页设置,不会沿用上一次请求的配置。 - 否则,如果当前标签页数少于
max_pages,就打开新标签页。 - 否则,以不到一秒的间隔检查是否有标签页空闲。等待 60 秒后仍不可用,就抛出
TimeoutError。这可能发生在目标网站无响应时。
出现错误的标签页会被关闭并替换。也可以随时调用 session.close_pages() 关闭所有标签页,下一次请求会打开新的标签页。
这样就能在同一个浏览器中同时获取多个 URL,节省资源,而且速度很快 :)
标签页保持打开,也意味着下次请求开始时,之前的页面仍然存在。下一次请求的 page_setup 会在离开旧页面前运行,因此可以把自动化操作串联到多次请求中。
在 0.3.2 到 0.4.14 版本中,请求后会关闭标签页,因为复用时可能残留前一次请求的设置。从 0.4.15 开始,每次复用都会重置设置,因此标签页可以保持打开。
保存和加载会话状态¶
使用 save_state(path) 把 Cookie、本地存储和 IndexedDB 保存到 JSON 文件。使用 load_state(path) 在另一个会话中恢复,或替换当前会话的已存储状态。两者都接受字符串或 pathlib.Path,并返回 None。文件格式与 Playwright 的存储状态文件兼容。
from scrapling.fetchers import StealthySession
with StealthySession() as session:
session.fetch('https://example.com')
session.save_state('state.json')
with StealthySession() as session:
session.load_state('state.json')
page = session.fetch('https://example.com')
登录或其他浏览器操作完成后再保存。在已启动的会话中调用这两个方法,并先等待所有抓取完成。加载会替换已有 Cookie、本地存储和 IndexedDB。要使用恢复的登录状态,需要获取或重新加载页面;加载状态本身不会让现有标签页跳转。
使用 AsyncStealthySession 时需要 await:
from scrapling.fetchers import AsyncStealthySession
async def restore_session():
async with AsyncStealthySession() as session:
await session.load_state('state.json')
page = await session.fetch('https://example.com')
await session.save_state('state.json')
return page
这些方法只保存和恢复共享浏览器上下文,不影响单次代理请求的临时上下文。使用 proxy_rotator 的会话没有共享上下文,因此会抛出 RuntimeError。固定的会话 proxy 可以正常使用。
文件不包含 sessionStorage、打开的标签页或浏览器设置。保存会覆盖文件,其父目录必须已存在。文件和 JSON 错误会直接抛给调用方。
会话的优点¶
- 复用浏览器:后续请求使用同一个浏览器实例,速度更快。
- 保留 Cookie:像普通浏览器一样自动管理 Cookie 和会话状态。
- 一致的指纹:所有请求使用相同的浏览器指纹。
- 节省内存:比每次请求都启动新浏览器更节省资源。
示例¶
通过示例更容易理解,下面逐一展示。
控制资源¶
Shadow DOM¶
有些页面把内容放在开放的 Shadow Root 中,这些内容不会出现在页面的普通 HTML 里。启用 pierce_shadow,即可把它们加入响应,再照常使用 CSS 或 XPath:
from scrapling.fetchers import StealthyFetcher
page = StealthyFetcher.fetch('https://example.com', pierce_shadow=True)
titles = page.css('product-card .title::text').getall()
此选项默认为 False。也可以为会话开启,再针对单次请求关闭:
from scrapling.fetchers import StealthySession
with StealthySession(pierce_shadow=True) as session:
page = session.fetch('https://example.com')
plain_page = session.fetch('https://example.com', pierce_shadow=False)
相同选项也适用于 async_fetch 和异步会话。
只包含开放的 Shadow Root,不会遍历封闭的 Shadow Root 或 iframe 文档。如果组件在页面加载后才出现,可以使用 wait_selector。
每个开放的 Shadow Root 会在响应中包装为一个 <shadow-root> 元素。Slot 保留原有标签和属性,并包含分配给它的节点;如果没有分配节点,则使用后备内容。未分配到 Slot 的 Light DOM 子节点会被省略。真实浏览器页面不会被修改。
<product-card>
<shadow-root>
<div class="title">Product name</div>
<slot name="price"><span class="price">20</span></slot>
</shadow-root>
</product-card>
后代选择器可以跨这些元素查找。直接子元素选择器则必须包含新增的包装元素和 Slot:
titles = page.css('product-card .title::text').getall()
titles = page.css('product-card > shadow-root > .title::text').getall()
prices = page.css('product-card > shadow-root > slot[name="price"] > .price::text').getall()
这些包装元素和 Slot 会出现在 XPath 路径、.children、response.body 和 HTML 导出中。Markdown 和文本提取只返回内容,不输出标签。表示隐藏状态的 Slot 属性仍会保留,供内容清理使用。
快照遵循内容顺序,但不计算 CSS 可见性或布局。包含 <shadow-root> 的选择器针对 Scrapling 响应,而不是真实浏览器页面。其他 HTML 解析器可能以不同方式处理这些特殊嵌套结构。
屏蔽域名¶
page = StealthyFetcher.fetch('https://example.com', blocked_domains={"ads.example.com", "tracker.net"})
控制网络¶
page = StealthyFetcher.fetch('https://example.com', network_idle=True)
page = StealthyFetcher.fetch('https://example.com', timeout=30000)
page = StealthyFetcher.fetch('https://example.com', proxy='http://username:password@host:port')
下载文件¶
page = StealthyFetcher.fetch('https://raw.githubusercontent.com/D4Vinci/Scrapling/main/docs/assets/main_cover.png')
with open(file='main_cover.png', mode='wb') as f:
f.write(page.body)
Response 对象的 body 属性始终返回 bytes。
导航前设置¶
如果事件监听器、路由或脚本必须在页面导航前注册,使用 page_setup。这个函数接收 page 对象,并在 page.goto() 之前运行。
from patchright.sync_api import Page
def capture_websockets(page: Page):
page.on("websocket", lambda ws: print(f"WebSocket opened: {ws.url}"))
page = StealthyFetcher.fetch('https://example.com', page_setup=capture_websockets)
from patchright.async_api import Page
async def capture_websockets(page: Page):
page.on("websocket", lambda ws: print(f"WebSocket opened: {ws.url}"))
page = await StealthyFetcher.async_fetch('https://example.com', page_setup=capture_websockets)
可以与 page_action 配合使用:page_setup 在导航前执行,page_action 在导航后执行。
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', # It can also be a dictionary with only the keys 'server', 'username', and 'password'.
)
solve_cloudflare 会自动检测并处理所有类型的 Cloudflare Turnstile 和 Interstitial 挑战:
- JavaScript 挑战,由 Cloudflare 管理
- 交互式挑战,例如点击验证框
- 无感挑战,在后台自动验证
也支持包含嵌入式验证码的自定义页面。
重要说明:
- 某些网站使用自定义实现,需要设置
wait_selector,确保解开验证码后继续等待真实页面内容。我们尽量让处理逻辑通用,但有些网站确实属于特殊情况。 - 使用 Cloudflare 处理功能时,超时应至少设为 60 秒,留出足够的挑战处理时间。
- 这项功能可以与代理和其他隐身选项一起使用。
浏览器自动化¶
这里需要了解 Playwright Page API。传入的函数接收 Playwright 的 Page 对象,执行所需操作,然后 Fetcher 继续工作。
如果启用了 network_idle,这个函数会在等待完成后立即执行。它在等待 wait_selector 之前执行,因此除了自动化,也可以用于其他页面修改。
下面通过页面的鼠标事件,先用滚轮滚动,再移动鼠标。
from patchright.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)
from patchright.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 = StealthyFetcher.fetch(
'https://quotes.toscrape.com/js-delayed/',
wait_selector='.quote',
wait_selector_state='visible'
)
wait_selector 传入 CSS 选择器,Fetcher 会等待 wait_selector_state 指定的状态。未指定时默认为 attached,即元素已经出现在 DOM 中。
之后,如果开启 load_dom,Fetcher 会再次检查所有 JavaScript 是否已加载并执行,也就是是否达到 domcontentloaded 状态,必要时继续等待。这个选项默认开启。如果启用了 network_idle,还会再次等待 network_idle 满足条件。
可以等待以下状态(来源):
attached:等待元素出现在 DOM 中。detached:等待元素从 DOM 中移除。visible:等待元素的边界框非空,且没有设置visibility:hidden。没有内容或设置了display:none的元素边界框为空,不算可见。hidden:等待元素从 DOM 中移除、边界框为空,或设置了visibility:hidden。它与'visible'相反。
网络历史记录¶
在浏览器会话中启用 record_requests,即可检查跨页面加载、操作和标签页的已完成请求。历史记录覆盖会话的所有上下文,包括临时代理上下文,并把响应保存为 Scrapling Response。可以查找 API 调用、HTTP 错误和重定向,而不必再次发送请求。会话关闭后也能读取已保存的响应头和响应体。失败或尚未完成的请求会被省略,已完成的 HTTP 4xx 和 5xx 响应则会保留。
from scrapling.fetchers import StealthySession
with StealthySession(record_requests=True, max_recorded_requests=1000) as session:
session.fetch('https://example.com', network_idle=True)
for response in session.network.search(resource_type='document', limit=None):
print(response.meta['network_id'], response.url, response.status)
if note := response.meta.get('body_note'):
print(note)
else:
print(response.body)
print(response.css('title::text').get())
session.network.clear()
同一 API 也适用于 AsyncStealthySession。记录功能默认关闭。创建会话时设置 record_requests=True,需要时再设置 max_recorded_requests。
session.network.search(url_pattern=None, method=None, resource_type=None, status=None, after_id=0, limit=100, include_static=True)按网络 ID 顺序返回已保存的Response。limit=None返回所有仍被保留的匹配项。url_pattern是搜索时使用的正则表达式,模式无效时会在搜索时抛出错误。after_id选择网络 ID 更大的响应。include_static=False只保留 XHR/fetch 请求和 HTTP 错误;使用True可包含所有资源类型和已保存的重定向。session.network.get(request_id)返回一个已保存的Response,如果记录已被移除,则返回None。session.network.clear()清空历史记录。请求 ID 仍继续递增,不会重复使用。
每个已保存的 Response 都可以直接访问 .url、.method、.status、.headers、.request_headers、字节形式的 .body、.json() 和 CSS/XPath 方法。.meta 包含 network_id、resource_type 和 request_body,后者为字节或 None。如果读取完整响应头失败,保存的头信息可能不完整。这些都是本地读取。即使在异步会话中,也不要使用 await 等待这些读取,或调用 .body()。如果保存的内容无法解析,原始字节仍会保留,但 CSS/XPath 方法会使用空文档。
要按 URL 查找 API 流量,可以使用 session.network.search(url_pattern=r'https://api\.example\.com/.*', limit=None)。只需要某种请求类型时,再加上 resource_type='fetch' 或 resource_type='xhr'。
记录器只读取并保存受支持的文本 Content-Type 响应体,包括 text/*、+json/+xml 类型及 SVG、JSON、XML、JavaScript、GraphQL 和 URL 编码的表单数据。二进制、未知类型或没有 Content-Type 的响应只保留元数据,不保留响应体字节。浏览器仍会正常加载资源。
保存的响应字节来自 Playwright,可能与服务器最初发送的字节不同。如果字节是有效的 UTF-8,就按 UTF-8 读取,与 Playwright 一致;否则使用声明的字符集。响应头保持不变。
被跳过、过大或无法读取的响应体返回 b'',原因保存在 response.meta.get('body_note')。非文本内容的说明是 Non-text body; not saved.。响应元数据和可用响应头仍会保留。已保存的文本,包括空文本,不带这个说明。HEAD 响应及状态码 204、205、304 的响应保持为空,也不带说明。
历史记录的 search、get 和 clear 都是本地同步调用,即使使用异步会话也一样。只有响应转换并保存后,条目才会出现。浏览器事件在 Playwright 活动期间处理,异步记录还需要事件循环运行。读取历史记录不会等待正在进行的记录操作。
关闭会话也不会等待记录操作。页面导航或浏览器上下文关闭时,尚未完成的记录可能丢失。已经保存的响应在关闭后仍可读取。
默认最多保留 1,000 个请求。每个响应体最多 1 MiB,所有响应体合计最多 20 MiB。达到限制时会删除旧记录。这些响应体大小限制是固定的,不能通过会话参数修改。它们只限制保留的条目和响应字节,不限制浏览器总内存、并行记录、临时完整读取、解析后的 HTML 或请求体。
迁移到 v5¶
v5 移除了 capture_xhr 和 response.captured_xhr。请创建独立的浏览器会话,设置 record_requests=True,再读取 session.network.search(url_pattern=..., limit=None)。这也替代了单次 Fetcher 调用的 API 记录方式,所以需要保留会话实例。过滤条件应用于整个会话的已保存历史,而不是记录阶段或某一次抓取。只保存受支持的文本响应体。search() 默认最多返回 100 个匹配项,limit=None 返回所有仍保留的匹配项,但不会改变历史记录的存储限制。
浏览器请求头和区域设置¶
page = StealthyFetcher.fetch(
'https://example.com',
google_search=True,
useragent='Mozilla/5.0...',
locale='en-US',
)
综合示例¶
from scrapling.fetchers import StealthyFetcher
def scrape_dynamic_content():
page = StealthyFetcher.fetch(
'https://example.com/dynamic',
network_idle=True,
wait_selector='.content'
)
content = page.css('.content')
return {
'title': content.css('h1::text').get(),
'items': [
item.text for item in content.css('.item')
]
}
代理轮换¶
from scrapling.fetchers import StealthySession, ProxyRotator
rotator = ProxyRotator([
"http://proxy1:8080",
"http://proxy2:8080",
"http://proxy3:8080",
])
with StealthySession(proxy_rotator=rotator, headless=True) as session:
page1 = session.fetch('https://example1.com')
page2 = session.fetch('https://example2.com')
page3 = session.fetch('https://example3.com', proxy='http://specific-proxy:8080')
Warning
所有浏览器 Fetcher 及其会话默认使用持久浏览器上下文和标签页池。但浏览器无法为每个标签页单独设置代理。因此,使用 ProxyRotator 时,Fetcher 会为每个代理创建独立上下文,每个上下文只有一个标签页。任务完成后,标签页和对应上下文都会关闭。
适用场景¶
以下情况适合使用 StealthyFetcher:
- 需要绕过反机器人防护。
- 需要可靠的浏览器指纹。
- 需要完整的 JavaScript 支持。
- 希望自动启用隐身功能。
- 需要浏览器自动化。
- 需要处理 Cloudflare 防护。