Scrapling StealthyFetcher 的 page_action 如何读取 init_script 写入的 window 全局变量
在 Scrapling 中用 StealthyFetcher 抓取页面时,init_script 和 page_action 是两个常用参数:init_script 指向一个 JavaScript 文件,在页面创建时执行;page_action 是一个接收 Playwright page 对象的函数,在导航之后运行。一个常见的需求是:让 init_script 先把某些数据写到 window 全局变量上,再在 page_action 里把值读回来。StealthyFetcher 文档明确指出一个坑:Stealthy 模式默认使用 Patchright 的隔离执行上下文(isolated execution context),因此 page_action 想读到 init_script 写进 window 的变量,必须用 page.evaluate(..., isolated_context=False),不能直接读取。下面按这条路径给出完整写法。
init_script 与 page_action 的执行时机
先确认两个参数在请求流程中的位置,它们决定了你能在 page_action 里读到什么:
init_script是注册到浏览器 context 上的(源码中通过 context 的add_init_script(path=...)实现,见 _base.py),所以它在页面创建时执行,对会话中的所有页面生效。文档对它的定义是:一个 JavaScript 文件的绝对路径。page_action在page.goto()导航完成、等待load_dom/network_idle(若启用)之后立即执行,且早于wait_selector的等待;如果启用了solve_cloudflare,它会先解决 Cloudflare 挑战再执行page_action(执行顺序见 fetch 流程)。- 因此,导航完成后
page_action运行时,init_script早已执行完毕,window上的变量已经存在——剩下的问题只是"在哪个执行上下文里读"。
单请求主路径:fetch 时同时传 init_script 和 page_action
准备一个 JavaScript 文件(内容自定,示例中的 myGlobalData 只是示例全局变量名,替换成你自己的 init 脚本实际写入的名字),保存后记住它的绝对路径。init_script 只接受绝对路径,相对路径不满足文档要求。
// init.js:init_script 指向的文件,示例内容
window.myGlobalData = {
source: 'init-script',
items: []
};
然后在 Python 侧传两个参数。page.evaluate 的第一个参数是要执行的 JS 表达式,即你在 init 脚本里写的全局变量名;isolated_context=False 是关键,它让表达式回到页面主世界执行,从而能拿到 init_script 写入的值:
from scrapling.fetchers import StealthyFetcher
from playwright.sync_api import Page
def read_init_global(page: Page):
# 第一个参数为 JS 表达式,即 init.js 写入 window 的变量名
value = page.evaluate('window.myGlobalData', isolated_context=False)
print(value) # 在 action 内打印确认读到的值,无固定预期输出
page = StealthyFetcher.fetch(
'https://example.com',
init_script='/absolute/path/to/init.js', # 替换为你 init.js 的绝对路径
page_action=read_init_global,
)
关于验证,文档没有给出固定成功日志,判断点在于执行上下文:按 StealthyFetcher 文档的说明,init_script 注册在浏览器 context 上、于页面创建时运行;而 Stealthy 模式默认使用 Patchright 的隔离执行上下文,所以从 page_action 读取该 window 全局变量时必须传 isolated_context=False。如果读出来的不是你在 init 脚本里写入的值,先检查两点:init_script 是否用了绝对路径、page.evaluate 是否漏了 isolated_context=False。
另一个需要注意的事实来自源码:page_action 抛出的异常会被 fetcher 捕获并记录为 Error executing page_action: ... 日志,而不会中断请求(见 _stealth.py)。所以不要依赖函数里抛异常来确认读取结果,应在 page_action 内把读到的值打印或记录下来,再据此判断。
异步版本
使用 async_fetch 时,传入的函数也必须是 async,page.evaluate 调用需要 await(文档在 DynamicFetcher 的 Browser Automation 一节说明了这一约定,StealthyFetcher 同为 Playwright 接口,规则一致):
from scrapling.fetchers import StealthyFetcher
from playwright.async_api import Page
async def read_init_global(page: Page):
value = await page.evaluate('window.myGlobalData', isolated_context=False)
print(value)
page = await StealthyFetcher.async_fetch(
'https://example.com',
init_script='/absolute/path/to/init.js', # 替换为你 init.js 的绝对路径
page_action=read_init_global,
)
可选分支:在 StealthySession 中配置
如果你要复用同一个浏览器发起多个请求,可以用 StealthySession。在会话类中,fetch 的全部参数都可以在构造会话时设置,但 init_script 不在文档列出的"可按单个请求覆盖"的参数列表里(该列表包含 page_action、timeout、wait 等,见 文档说明),所以 init_script 应作为会话级配置传入,page_action 则可以在每次 session.fetch 时指定:
from scrapling.fetchers import StealthySession
def read_init_global(page: Page):
value = page.evaluate('window.myGlobalData', isolated_context=False)
print(value)
with StealthySession(
init_script='/absolute/path/to/init.js' # 会话级配置,对该会话所有页面生效
) as session:
page = session.fetch('https://example.com', page_action=read_init_global)
限制与边界
init_script必须是 JavaScript 文件的绝对路径,且作用于会话中的所有页面,不能按 URL 区分不同脚本。isolated_context=False是 Stealthy 模式下的必要处理,因为默认执行上下文是隔离的;文档没有为其他场景(如DynamicFetcher)给出相同约定,本文不展开。page_action内部的异常只会被记录日志,请求仍会返回Response对象,排查时以日志中的Error executing page_action:为准。page_action执行时页面已完成load_dom(默认启用)等待,此时init_script一定已经运行过;不需要为"脚本还没执行"额外加等待。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00