logo

自动化爬虫被检测?中间人代理改写JS破局

作者:问题终结者2026.11.06 20:06浏览量:0

简介:浏览器自动化采集常因 navigator.webdriver 等环境特征被风控脚本识别。本文剖析检测原理,给出一套基于本地中间人代理拦截并改写检测 JS 的完整方案:从定位特征脚本、编写响应改写钩子,到前置注入、指纹与行为加固、验证方法与合规边界,帮助读者系统性提升自动化采集的隐蔽性与稳定性。

一、问题背景:自动化浏览器为什么会被“一眼看穿”

在数据采集场景中,登录态是绕不开的一环:只有完成登录、拿到有效 Cookie,才能访问价格、评价等核心接口。而登录环节几乎必然遭遇滑块验证、行为校验等风控手段,很多团队会选择用浏览器自动化工具(驱动真实浏览器按脚本执行操作)来模拟人工流程。

问题在于,成熟的风控系统对这类自动化环境有一整套识别手段,常见检测点包括:

  1. 规范属性暴露:WebDriver 规范(浏览器自动化协议)定义了 navigator.webdriver 属性,浏览器被驱动接管时该值为 true,等于“自报家门”。
  2. 注入残留:自动化驱动启动时会向页面注入辅助脚本,在 window、document 上留下 driver_evaluate、webdriver_evaluate 等一系列特征属性,部分驱动还会挂载以 $cdc 开头的疑似随机属性。
  3. 静态特征扫描:风控 JS 会把上述特征串硬编码进代码,运行时枚举 window/document 属性逐个比对,一旦命中即判定为机器环境,轻则对请求返回错误响应,重则触发验证码甚至封禁账号。
  4. 环境指纹异常:UA 与真实内核不匹配、缺少正常浏览器应有的插件与字体、Canvas/WebGL 指纹异常、时区语言不一致等。
  5. 行为与网络特征:鼠标轨迹过于平直、点击间隔均匀、请求频率规律化、出口 IP 为数据中心地址等。

其中前三类属于“环境特征”,识别成本最低、命中率最高,也是大多数初学者卡住的地方。

二、常见应对思路与它们的天花板

面对检测,常见做法有三种,各有局限:

  1. 修改启动参数:通过浏览器命令行开关关闭“受控模式”相关特性,可以让 navigator.webdriver 返回 false。但它只能解决单一检测点,注入残留等其他特征依然存在。
  2. 前置注入脚本:利用自动化框架提供的“页面脚本执行前注入”能力,用 Object.defineProperty 把 navigator.webdriver 重定义为 false,并顺带删除部分特征属性。覆盖面比前者大,但风控 JS 一旦深度混淆、多文件加载或校验自身完整性,逐点对抗会非常疲惫。
  3. 网络层釜底抽薪:既然检测逻辑写在某段 JS 里,那就让这段 JS “照常运行,但看不见任何特征”。在流量链路上对响应体做改写,把特征串统一替换成无害占位符,这就是本文的重点:基于本地中间人代理的方案。

思路三的优势在于:不与风控代码逐属性缠斗,而是让它扫描的目标批量“消失”;同时改写发生在响应到达浏览器之前,对页面逻辑完全透明。

三、中间人代理方案原理

中间人代理(MITM Proxy)在本机启动一个代理进程,浏览器所有 HTTP/HTTPS 流量先经过它再发往服务器。代理持有自己签发的 CA 证书,因此能够解密 TLS 流量,查看并修改请求与响应。多数此类工具都支持“脚本钩子”:请求到达时触发 request 钩子,响应返回时触发 response 钩子,我们正是在 response 钩子里对检测 JS 做字符串替换。

一个关键细节:很多风控 JS 除了检测,还承担生成签名、Token 等职责。如果把整个文件直接拦截掉(返回空内容),往往导致后续接口签名缺失、请求全部失败。更稳妥的做法是“改写而非删除”——让脚本继续执行,只是它要找的特征全部不存在。

四、实战步骤

步骤 1:环境准备

  • 准备 Python 环境,通过包管理器安装一款支持 Python 脚本扩展的中间人代理工具(此类工具多为开源项目,选择活跃维护的即可)。
  • 按工具文档生成 CA 证书,并在本机系统/浏览器中信任该证书,否则 HTTPS 流量无法解密。注意:信任自签证书仅限自己的测试环境,不要在不受控设备上使用。

步骤 2:让流量走代理

  • 将系统代理或浏览器代理指向 127.0.0.1:8001(端口可自定)。
  • 自动化浏览器也可以在启动参数中指定代理,保证被驱动的浏览器同样经过代理链路。

步骤 3:启动代理监听
以监听模式启动代理并指定端口(各工具命令不同,以所用工具文档为准),确认日志中出现监听 8001 端口的提示。

步骤 4:定位检测脚本

  • 打开目标网站,用浏览器开发者工具的 Sources/Network 面板全局搜索特征词,如 webdriver、driver-evaluate、headless 等。
  • 找到命中最集中、URL 相对稳定的那份 JS(下文以 risk-check.js 代称)。实践中它通常是一个体积较大、深度混淆的风控脚本,可能由独立路径甚至独立域名发布。
  • 记录其 URL 特征片段,改写规则建议匹配路径前缀而非完整文件名,以便兼容版本号变化。

步骤 5:编写改写脚本

  1. # driver_pass.py
  2. # 说明:不同代理工具的脚本接口略有差异,下面以常见的
  3. # "response(flow) 钩子"约定演示核心逻辑,接口名以所用工具文档为准。
  4. import logging
  5. logger = logging.getLogger("driver_pass")
  6. # 特征串清单:风控 JS 会逐一枚举这些属性来判断自动化环境
  7. WEBDRIVER_KEYS = [
  8. "webdriver",
  9. "__driver_evaluate",
  10. "__webdriver_evaluate",
  11. "__driver_unwrapped",
  12. "__webdriver_unwrapped",
  13. "_WEBDRIVER_ELEM_CACHE",
  14. "driver-evaluate",
  15. "webdriver-evaluate",
  16. "webdriverCommand",
  17. "webdriver-evaluate-response",
  18. "__webdriverFunc",
  19. "__webdriver_script_fn",
  20. "__$webdriverAsyncExecutor",
  21. # 部分驱动还会在 window 上留下以 $cdc 开头的特征属性,
  22. # 以及包含具体驱动名称的字符串,可按需扩充清单。
  23. ]
  24. def response(flow):
  25. # 1) 只处理命中的风控脚本,避免误伤正常业务 JS
  26. if "risk-check" not in flow.request.url:
  27. return
  28. text = flow.response.text
  29. hit = False
  30. # 2) 把带引号的属性名替换成无害占位符
  31. for key in WEBDRIVER_KEYS:
  32. token = '"{}"'.format(key)
  33. if token in text:
  34. text = text.replace(token, '"NO-SUCH-ATTR"')
  35. hit = True
  36. # 3) 处理压缩代码中的布尔探测:navigator 被压缩成短变量 t,
  37. # 出现 t.webdriver 的访问,直接替换为 false,
  38. # 让 "if (t.webdriver)" 走"非自动化"分支
  39. if "t.webdriver" in text:
  40. text = text.replace("t.webdriver", "false")
  41. hit = True
  42. # 4) 防止旧 JS 被缓存,导致改写不生效
  43. flow.response.headers["Cache-Control"] = "no-store"
  44. if hit:
  45. logger.info("rewritten: %s", flow.request.url)
  46. flow.response.text = text

几个工程细节需要强调:

  • 替换时带上双引号包裹,避免把页面正文或注释里恰好出现的 webdriver 字样误伤;
  • “t.webdriver” 这类短变量替换存在误伤风险(其他代码可能出现同样文本),生产环境建议改用带单词边界的正则替换;
  • 日志用于确认命中情况,调试期务必打开,规则失效时它是第一现场。

步骤 6:带脚本重启代理并验证

  • 退出原代理进程,加载改写脚本重新启动,仍监听 8001 端口。
  • 清理浏览器缓存后访问目标站,在控制台执行:
  1. navigator.webdriver // 期望 false 或 undefined
  2. Object.keys(window).filter(k => /webdriver|driver/i.test(k)) // 期望空数组
  • 完整走一遍登录与滑块流程,观察接口是否恢复正常响应。

五、别只改 JS:配套加固清单

改写检测脚本解决的是“环境特征”,但要长期稳定运行,还需叠加以下手段:

  1. 前置注入:在页面任何脚本执行前,用 Object.defineProperty 把 navigator.webdriver 重定义为 false 或 undefined,并清理已知残留属性,与网络层改写形成双保险。
  2. 指纹一致性:UA、平台、时区、语言保持自洽;必要时处理 Canvas/WebGL 指纹与插件列表,避免“参数说了谎、指纹说实话”。
  3. 行为拟人:随机化操作间隔、用贝塞尔曲线模拟鼠标轨迹、加入滚动与停顿,避免匀速直线式的机器操作。
  4. 网络层治理:使用干净的代理 IP 池,控制并发与频率,避免规律化请求模式。
  5. 版本监控:风控 JS 会不定期更新特征与文件名,建议对响应体做抽样 diff,一旦规则命中率归零,及时更新特征清单。

六、规模化与工程化建议

当采集任务从单机走向规模化,可以把上述链路迁移到云上:采集程序部署在云服务器,改写代理作为旁路组件统一维护;抓取结果写入对象存储,过程日志接入日志服务,规则命中率与任务成功率接入监控告警,任务分发通过消息队列解耦。这些能力属于通用云产品品类,主流云厂商(如百度智能云)均有对应服务,按需组合即可,无需绑定特定实现。

七、合规提醒

反检测技术本身中立,但使用有边界:仅在你有权访问的系统上、以遵守服务条款与 robots 协议为前提开展采集;控制请求频率,不抓取个人隐私与敏感数据,不对目标系统造成干扰或破坏。理解攻防原理的价值在于构建更健壮的工程能力,落地时请务必守住法律与职业道德底线。

发表评论

活动