拓冰建站拓冰建站
首页 / 资讯中心 / 正文

瑞数5代反爬破解:补环境与__rsc后缀生成实战指南

简介本资源聚焦瑞数5代rs5动态反爬机制的实战破解面向中高级Python爬虫开发者与Web安全研究者解决rs5环境补全、动态Cookie生成及URL后缀算法还原等核心难点。压缩包共7个文件含5个JavaScript脚本涵盖rs5调试器、环境模拟与后缀生成逻辑和2个Python脚本基于pyexecjs调用JS执行、封装调试流程总大小881KB结构紧凑、即拿即用。已有1080人学习下载体现其在真实业务场景如房地产行业数据采集中的高实用性。读者可直接复用JS环境补全逻辑、Python调用封装模板及调试器源码快速集成到自有爬虫项目中同时通过debugger_bak与dev系列脚本对比深入理解rs5多版本环境变量差异与算法演进路径掌握从逆向分析到稳定落地的完整技术链路。1. 瑞数5代rs5不是“加密”而是环境校验补环境后缀算法才是真实对抗焦点很多刚接触瑞数反爬的开发者会误以为 rs5 是一套“JS 加密算法”花大量时间逆向混淆后的eval或atob链结果发现即使还原出原始逻辑请求仍被拦截——因为 rs5 的核心防御不在“算什么”而在“谁在算”。它通过深度检测浏览器运行时环境如window属性完整性、navigator对象真实性、document构造函数可写性、Function.prototype.toString是否被篡改构建可信度评分再结合动态生成的后缀参数非固定 token而是由当前页面 DOM 结构、JS 执行上下文、时间戳、随机 salt 共同参与计算形成双重校验闭环。这意味着单纯用 requests 模拟 headers 无效仅 patch 个别 JS 函数也不够必须让自动化环境具备“类真实浏览器”的行为一致性。本文面向已掌握 Selenium/Playwright 基础、正卡在 rs5 请求 403 或 503 的 Python 爬虫工程师聚焦可落地的补环境策略与后缀算法复现路径不讲概念堆砌只拆解你调试器里真正能看到的原型链篡改点、toString劫持位置、以及__rsc参数生成时的 DOM 依赖项。2. 补环境三要素原型链修复、函数 toString 恢复、DOM 属性注入瑞数5代对环境的校验远超常规反爬其检测脚本会在页面加载早期执行多轮探测重点检查三类对象状态window的不可枚举属性是否缺失、navigator的 getter 是否被重写、document的createElement等方法是否指向原生实现。若任一环节异常后续后缀计算直接跳过返回空值或伪造值导致服务端校验失败。补环境不是“打补丁”而是重建浏览器对象模型的可信基线。2.1 识别被篡改的原型链从Object.prototype到HTMLDocument.prototype瑞数常通过Object.defineProperty隐藏关键属性如window.__SENTRY__、navigator.permissions或劫持Object.getPrototypeOf返回伪造原型。实际调试中可在控制台执行Object.getOwnPropertyNames(window).filter(k k.startsWith(__) || k.includes(sentry)) // 输出类似[__RS5__, __rsc, __SENTRY__]这些属性名即为瑞数注入的校验标记。更隐蔽的是对HTMLDocument.prototype的篡改瑞数会重写document.createElement使其在创建特定标签如script、iframe时注入校验逻辑。验证方式是document.createElement.toString() // 若返回 function createElement() { [native code] } 则正常若含自定义逻辑则已被劫持补环境关键动作在 Puppeteer/Playwright 启动后、加载目标页前注入以下 JS 片段重置原型链// 注入时机page.addInitScript() const resetPrototype () { // 恢复 window 原型链 Object.setPrototypeOf(window, Window.prototype); // 恢复 document 原型链 Object.setPrototypeOf(document, HTMLDocument.prototype); // 恢复 navigator 原型链 Object.setPrototypeOf(navigator, Navigator.prototype); // 删除瑞数注入的私有属性 delete window.__RS5__; delete window.__rsc; delete window.__SENTRY__; }; resetPrototype();提示此脚本必须在页面任何 JS 执行前注入使用page.addInitScript()而非page.evaluate()否则瑞数检测脚本已运行完毕补救无效。2.2 恢复 Function.prototype.toString 的原生行为瑞数通过篡改Function.prototype.toString来检测代码注入痕迹。正常情况下该函数返回function xxx() { [native code] }但被篡改后可能返回空字符串、固定字符串或抛出错误。检测方式const fn () {}; console.log(fn.toString()); // 若输出非 [native code] 格式则被劫持补环境关键动作在addInitScript中同步恢复toString// 继续在 resetPrototype 后添加 const originalToString Function.prototype.toString; Object.defineProperty(Function.prototype, toString, { value: originalToString, configurable: false, writable: false, enumerable: false });注意configurable: false是必须的瑞数会检查该属性是否可配置若设为true后续校验仍会失败。2.3 注入缺失的 DOM 属性与方法瑞数校验常依赖document上的非常规属性如document.hidden、document.visibilityState、document.hasFocus()返回值。Headless 浏览器默认这些值为undefined或false需主动注入// 在 init script 中补充 Object.defineProperty(document, hidden, { value: false, writable: false }); Object.defineProperty(document, visibilityState, { value: visible, writable: false }); document.hasFocus () true; // 补充 createElement 的原生引用 document.createElement HTMLDocument.prototype.createElement.bind(document);表格瑞数5代高频校验的 DOM 属性及推荐注入值属性/方法正常值注入方式校验目的document.hiddenfalseObject.defineProperty(document, hidden, {value: false})检测页面是否处于后台document.visibilityStatevisibleObject.defineProperty(document, visibilityState, {value: visible})防止 visibility API 欺骗navigator.webdriverundefinedObject.defineProperty(navigator, webdriver, {get: () undefined})绕过 WebDriver 检测window.outerWidth/outerHeight接近innerWidth/innerHeightObject.defineProperty(window, outerWidth, {value: window.innerWidth})检测窗口尺寸异常3. 后缀算法解析从 DOM 结构哈希到动态 salt 生成瑞数5代的后缀参数通常名为__rsc并非静态 token而是由当前页面 DOM 快照 时间戳 随机 salt 三次哈希生成。其核心逻辑在rs5.js中但关键输入源来自页面实时状态因此必须在目标页完全加载后、发起请求前计算。3.1 定位后缀生成入口window.__rsc的赋值时机在 Chrome DevTools 的 Sources 面板中全局搜索__rsc 或window.__rsc 通常会定位到类似代码window.__rsc (function() { const t Date.now(); const e document.documentElement.outerHTML.substring(0, 1000); // 截取 DOM 前1000字符 const n Math.random().toString(36).substr(2, 8); // 8位随机salt return md5(t e n); // 实际为自定义哈希非标准md5 })();注意document.documentElement.outerHTML是关键输入意味着每次 DOM 变化如 AJAX 渲染新内容都会导致后缀变更。因此必须在目标数据渲染完成后再计算__rsc而非页面加载完成时。3.2 Python 端复现哈希逻辑避开 JS 引擎调用的三种方案直接在 Python 中复现瑞数的自定义哈希常为多次 XOR 字符串旋转极容易因 JS 运行时差异失败。更可靠的做法是在浏览器内执行计算Python 仅接收结果。以下是三种主流方案对比方案实现方式优点缺点适用场景Playwright evaluatepage.evaluate(() window.__rsc)无需额外依赖与页面环境完全一致依赖__rsc已存在若未生成则报错页面加载后立即可用注入计算函数page.addScriptTag(content: window.genRsc () {...})可控性强支持传参定制需手动维护 JS 逻辑更新DOM 动态变化后需重新计算CDP 调用 Runtime.evaluatepage._channel.send(Runtime.evaluate, {...})最底层绕过 Playwright 封装API 不稳定Playwright 版本升级易失效高级用户调试推荐方案Playwright 动态计算from playwright.sync_api import sync_playwright import time def get_rsc_suffix(page): # 等待目标数据元素出现如商品列表容器 page.wait_for_selector(#product-list, statevisible, timeout10000) # 在页面上下文中执行后缀生成确保 DOM 已就绪 rsc page.evaluate(() { // 复制瑞数原始逻辑此处为简化示意 const t Date.now(); const e document.documentElement.outerHTML.substring(0, 1000); const n Math.random().toString(36).substr(2, 8); // 实际需替换为瑞数使用的哈希函数如自定义 rotateXOR return btoa(t e n).substring(0, 16); // 示例非真实算法 }) return rsc with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://target-site.com/list) rsc_value get_rsc_suffix(page) # 获取后缀 # 发起带后缀的请求 response page.goto(fhttps://target-site.com/api/data?__rsc{rsc_value})说明page.evaluate内的 JS 代码必须严格复现瑞数原始逻辑中的哈希步骤。若瑞数使用了CryptoJS.SHA256等库需提前注入该库若为自定义位运算则需在 Python 中用ctypes或bitarray精确复现——但优先选择在浏览器内计算避免跨语言精度误差。3.3 后缀参数的生命周期管理何时刷新、何时复用__rsc并非一次生成永久有效。瑞数服务端会校验其时间戳成分Date.now()若请求时间与生成时间相差超过 30 秒直接拒绝。因此禁止缓存__rsc值超过 25 秒每个请求必须独立生成__rsc不能多个请求共用同一后缀AJAX 分页时每页需重新计算因 DOM 结构变化验证方式抓包对比两次请求的__rsc值若相同且间隔 25s必失败。4. Python 爬虫集成Playwright 自动化流程与异常处理将补环境与后缀生成整合进完整爬虫流程需解决三个实际问题如何保证初始化脚本执行顺序、如何捕获瑞数拦截响应、如何优雅重试。以下为生产级代码骨架。4.1 初始化脚本的加载顺序与依赖管理Playwright 的add_init_script会注入到每个 frame但瑞数检测脚本常位于主 frame 的script标签中。必须确保补环境脚本在瑞数脚本执行前生效。正确顺序def setup_rs5_environment(page): # Step 1: 注入原型链修复脚本最早执行 page.add_init_script( Object.setPrototypeOf(window, Window.prototype); Object.setPrototypeOf(document, HTMLDocument.prototype); Object.setPrototypeOf(navigator, Navigator.prototype); delete window.__RS5__; delete window.__rsc; ) # Step 2: 注入 toString 恢复紧随其后 page.add_init_script( const original Function.prototype.toString; Object.defineProperty(Function.prototype, toString, { value: original, configurable: false, writable: false, enumerable: false }); ) # Step 3: 注入 DOM 属性最后因依赖前两步 page.add_init_script( Object.defineProperty(document, hidden, {value: false}); Object.defineProperty(document, visibilityState, {value: visible}); Object.defineProperty(navigator, webdriver, {get: () undefined}); document.hasFocus () true; ) # 使用示例 with sync_playwright() as p: browser p.chromium.launch(headlessTrue, args[--disable-blink-featuresAutomationControlled]) page browser.new_page() setup_rs5_environment(page) # 必须在 goto 前调用 page.goto(https://target.com)注意args[--disable-blink-featuresAutomationControlled]是必需的否则navigator.webdriver为true瑞数直接拦截。4.2 拦截响应识别与自动重试机制瑞数拦截返回码不固定常见为403 Forbidden、503 Service Unavailable或200但响应体含{code:403,msg:invalid rsc}。需在请求后主动校验def safe_fetch_data(page, url): for attempt in range(3): try: # 生成新后缀 rsc page.evaluate(generate_rsc_js) # generate_rsc_js 为前述 JS 字符串 full_url f{url}?__rsc{rsc} # 发起请求并等待响应 response page.goto(full_url, wait_untilnetworkidle, timeout15000) # 检查响应内容 content response.text() if code:403 in content or msg:invalid rsc in content: raise ValueError(Invalid __rsc detected) if response.status ! 200: raise ValueError(fHTTP {response.status}) return content except Exception as e: print(fAttempt {attempt 1} failed: {e}) if attempt 2: time.sleep(1) # 退避 continue else: raise e # 调用 data safe_fetch_data(page, https://api.target.com/items)4.3 瑞数6代兼容性提示DOM 快照粒度升级最新网络热词中频繁提及“瑞数6”其核心变化是将 DOM 校验从outerHTML升级为document.querySelectorAll(*).length document.styleSheets.length performance.memory.totalJSHeapSize的组合哈希。这意味着仅截取outerHTML前1000字符已不足必须获取全部样式表数量与 JS 堆内存快照performance.memory在无痕模式下不可用需启用--enable-precise-memory-info对应 Python 侧需扩展generate_rsc_jsconst domHash document.querySelectorAll(*).length document.styleSheets.length (performance.memory ? performance.memory.totalJSHeapSize : 0); return customHash(Date.now() domHash Math.random());5. 关键调试技巧快速定位补环境失效点与后缀生成偏差当请求仍被拦截时90% 的问题集中在两个环节补环境未生效或后缀计算与瑞数服务端不一致。以下技巧可将排查时间从小时级压缩至分钟级。5.1 三步验证补环境是否成功在 Playwright 中启用devtoolsTrue手动打开控制台执行以下命令逐项验证检查原型链Object.getPrototypeOf(window) Window.prototype // 应返回 true Object.getPrototypeOf(document) HTMLDocument.prototype // 应返回 true检查 toString 行为const testFn () {}; testFn.toString() function () { [native code] } // 应返回 true检查 DOM 属性document.hidden // 应为 false而非 undefined navigator.webdriver // 应为 undefined而非 true若任一为false说明对应补环境脚本未执行或被覆盖。5.2 后缀偏差比对导出浏览器端与 Python 端计算中间值在page.evaluate中打印各输入变量与 Python 本地日志比对# 在 generate_rsc_js 中添加调试输出 page.evaluate(() { const t Date.now(); const e document.documentElement.outerHTML.substring(0, 1000); const n Math.random().toString(36).substr(2, 8); console.log(RS5_DEBUG:, {t, e_len: e.length, n}); // 输出到浏览器控制台 return customHash(t e n); })同时在 Python 中记录相同变量print(fPython_DEBUG: t{int(time.time() * 1000)}, e_len?, n?) # e_len 需通过 page.content() 获取比对t值应相差 100ms、n值若不同则随机数种子不一致、e_len若差异大说明 DOM 截取时机不同。5.3 瑞数无限 debug 的绕过禁用 source map 与断点注入网络热词“瑞数的无限debug怎么去掉”实指瑞数在rs5.js中插入大量debugger语句导致自动化工具卡死。解决方案不是删除 debugger会触发校验而是禁用调试器介入# Playwright 启动时禁用 JS 调试 context browser.new_context( java_script_enabledTrue, # 关键禁用 devtools 相关功能 bypass_cspTrue, ignore_https_errorsTrue ) page context.new_page() # 注入脚本屏蔽 debugger page.add_init_script(debugger function(){};)注意debugger function(){}必须在所有其他脚本前注入否则瑞数会重写该赋值。最终一个能稳定过瑞数5代的 Python 爬虫其核心不在于“破解算法”而在于精确模拟浏览器环境的行为契约——这要求你像前端工程师一样理解原型链、像安全研究员一样分析 DOM 操作、像运维工程师一样管理请求生命周期。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门