简介:在自动化采集场景中,Selenium驱动的浏览器常因暴露WebDriver特征而被反爬脚本识别,导致无法正常获取页面数据。本文从识别原理出发,梳理一套可落地的对抗方案,重点介绍基于本地代理拦截并改写反爬脚本的实践方法,帮助开发者降低被识别的概率,恢复数据采集流程的稳定性。
Selenium作为广泛使用的浏览器自动化方案,在数据采集、页面测试、流程自动化等场景中非常常见。它通过驱动真实浏览器执行脚本,能够处理JavaScript渲染、用户交互、Cookie写入等传统HTTP请求难以完成的任务。然而,也正因为Selenium需要控制浏览器,其运行环境与普通用户浏览器存在可观测的差异。
许多平台会在页面中嵌入检测脚本,通过分析浏览器运行时的若干特征判断当前会话是否由自动化工具驱动。典型特征包括:
navigator.webdriver 属性被设置为 true;__driver_evaluate、__webdriver_evaluate、__selenium_evaluate 等内部函数;document 下存在 $cdc_ 前缀的隐藏属性;window 对象中存在与自动化工具相关的状态标记;一旦检测脚本确认当前浏览器具备上述特征,就可能拒绝返回真实数据,转而返回空白页、错误提示、验证码或伪造内容,导致自动化流程中断。
面对Selenium特征被识别的问题,目前业界常见的处理方式主要有以下几种:
部分驱动在启用开发者模式后会关闭部分自动化标识,但现代检测脚本已经具备更复杂的特征识别能力,单靠开发者模式往往无法完全消除被识别的风险。
可以通过在页面加载前注入JavaScript,将 navigator.webdriver 重定义为 false。但这种方式存在时序问题:如果检测脚本在当前页面文档创建时就已经读取了该属性,那么事后修改无法奏效。
一些技术团队会通过修改浏览器驱动源码,移除自动化特征后再重新编译。该方案效果较好,但维护成本较高,且驱动版本升级后需要重新适配,对于快速迭代的采集项目不够友好。
本文重点讨论的方案,是在浏览器与服务器之间增加一层本地代理,对响应内容中的检测脚本进行实时改写,从根源上移除识别逻辑。该方案不修改浏览器驱动,也不改动Selenium代码,具有较高的灵活性和可维护性。
当浏览器请求页面时,服务器会返回HTML文档,其中通常包含若干外部JavaScript文件。这些脚本中有一部分专门负责检测自动化特征。如果我们能够在响应返回给浏览器之前,对这些脚本内容进行过滤和替换,就可以使检测逻辑失效。
具体流程如下:
webdriver 相关标识改写为无效内容;这种方式的核心优势在于,它不依赖浏览器启动参数,也不修改驱动文件,而是从网络层面对检测逻辑进行干预。即使目标网站更新了前端脚本,只要关键特征没有发生根本性变化,拦截规则仍然可以继续生效。
可以选用支持脚本扩展的本地代理工具。以Python生态为例,常用的方式是安装一个具备拦截能力的代理框架。基本安装命令如下:
pip install mitmproxy
安装完成后,需要将本机网络代理设置为 127.0.0.1,端口可以选择 8001 或其他未被占用的端口。设置代理的目的是让浏览器发出的所有请求都经过本地代理,从而获得对响应内容的控制权。
在Windows环境下,可以使用命令行启动代理:
mitmdump -p 8001
在Linux环境下,可以使用:
mitmproxy -p 8001
启动成功后,代理就开始监听指定端口。此时需要确保浏览器已经正确配置代理,并且代理证书已被信任,才能正常解析HTTPS内容。
打开浏览器的开发者工具,切换到Network面板,刷新目标页面,观察加载的JavaScript文件列表。通常检测逻辑会集中在一个或多个名称中包含特征词的文件中。例如,某些页面会加载一个路径中包含 /js/yoda. 的脚本文件,该文件负责处理与WebDriver相关的请求和响应。
开发者的任务是找到具体负责检测自动化的脚本,记录其URL路径特征。后续的拦截规则将基于这些特征进行匹配。
在代理工具中,可以通过响应钩子对匹配到的内容进行实时修改。以下是一个参考实现:
import refrom mitmproxy import ctxdef response(flow):if '/js/yoda.' in flow.request.url:for key in ['webdriver','__driver_evaluate','__webdriver_evaluate','__selenium_evaluate','__fxdriver_evaluate','__driver_unwrapped','__webdriver_unwrapped','__selenium_unwrapped','__fxdriver_unwrapped','_Selenium_IDE_Recorder','_selenium','calledSelenium','_WEBDRIVER_ELEM_CACHE','ChromeDriverw','driver-evaluate','webdriver-evaluate','selenium-evaluate','webdriverCommand','webdriver-evaluate-response','__webdriverFunc','__webdriver_script_fn','__$webdriverAsyncExecutor','__lastWatirAlert','__lastWatirConfirm','__lastWatirPrompt','$chrome_asyncScriptInfo','$cdc_asdjflasutopfhvcZLmcfl_']:ctx.log.info('Remove "%s" from %s.' % (key, flow.request.url))flow.response.text = flow.response.text.replace('"%s"' % key, '"NO-SUCH-ATTR"')flow.response.text = flow.response.text.replace('t.webdriver', 'false')flow.response.text = flow.response.text.replace('ChromeDriver', '')
上述脚本的处理逻辑如下:
t.webdriver 相关内容替换为 false;替换后的脚本将无法通过特征匹配来识别自动化环境,从而降低被检测的概率。
完成脚本编写后,需要重新启动代理,并加载该拦截规则。例如:
mitmdump -s DriverPass.py -p 8001
其中 -s 参数用于指定要加载的脚本文件。代理启动后,所有经过该端口的请求和响应都会经过自定义逻辑处理。
之后,再启动Selenium浏览器,使用相同的代理配置访问目标网站。此时检测脚本已经被拦截和改写,页面更有可能正常返回数据。
目标网站的前端脚本可能会不定时更新。建议将需要拦截的关键特征维护在一个独立的配置文件中,当检测逻辑发生变化时,只需调整配置,而不必重新编写核心代理逻辑。这样可以显著降低维护成本。
除了JavaScript特征检测,一些平台还会结合浏览器指纹、请求频率、访问行为等进行综合判断。因此,仅靠拦截脚本并不能保证百分之百不被识别。在实际项目中,建议结合以下措施:
在采集任务中,不宜使用无头模式或默认的自动化参数组合。这些配置会进一步放大环境差异,增加被识别风险。如果必须使用无头模式,建议对浏览器指纹相关参数进行定制化调整。
自动化采集应严格遵守目标平台的服务条款和法律法规。本文介绍的技术仅用于技术研究和合法的数据获取场景。在实际应用中,开发者应当评估数据使用的合规性,避免对他人服务造成负面影响。
Selenium被检测的核心原因在于其运行环境与正常用户浏览器存在可识别的差异。通过本地代理拦截并改写前端检测脚本,可以从网络层消除部分自动化特征,从而降低被识别的概率。该方案不需要修改驱动源码,也不依赖特定启动参数,具有较好的灵活性和可维护性。
当然,反检测不是单一动作,而是一个系统性工程。除了拦截检测脚本,还需要在请求行为、浏览器指纹、会话管理等方面进行综合优化,才能构建更加稳健的自动化采集流程。