☰
SeleniumBase实战:UC模式与Headless模式选型及避坑指南
2026/10/21 19:34:36 网站建设 项目流程

做自动化测试和网页数据采集的人,十有八九都遇到过那扇让人头疼的验证页——脚本刚打开浏览器,还没操作两步,迎面就是一个人机验证,或者干脆给你返回一个空页面。“你是不是机器人?”这个问题很打击人,因为我们明明就是在用浏览器跑测试,凭什么就被当成机器人?

这背后其实就是反爬与反自动化检测机制在起作用。网站通过浏览器指纹、行为特征、UA、WebDriver标记这些信息,把“真人访问”和“自动化访问”区分开。而SeleniumBase里恰好有两种模式,对应两条完全不同的思路:UC模式(Undetected Chrome)和Headless模式(无头浏览器模式)。

这篇文章就围绕这两个模式展开,把它们的原理、参数、适用场景、常见坑一个一个摊开讲。先说明白立场:本文讲的是自动化测试过程中如何减少脚本被网站误判,不是教你绕过风控去干破坏的事。无论做测试还是数据采集,请只在自己有权限的系统、测试环境或公开页面上操作,遵守网站的robots协议和用户协议。合规这条线,每个人都要守住。

这篇文章适合谁看?不管是做UI自动化测试的同学,还是写合规网页数据采集脚本的工程师,只要你在用Selenium系工具,这篇文章都应该能帮上忙。

1. 先搞清楚:UC模式和Headless模式到底解决什么问题

1.1 自动化测试为什么会被误判

先别急着敲代码,把底层原因想明白,后面能少踩80%的坑。

Selenium这类自动化工具为了能远程控制浏览器,会暴露许多“自动化特征”,这些特征是摆在明面上的:

  • navigator.webdriver这个属性,在自动化模式下是true,真人浏览器通常是undefined
  • User-Agent里可能带HeadlessChrome字样(老版无头模式尤其明显)
  • window.chrome这个对象,在部分自动化场景下是缺失的
  • 浏览器进程启动参数、远程调试端口、CDP连接等,都可能暴露自动化痕迹

网站的检测代码只要在页面里嵌入一段JS,就能把这些特征查个底朝天。所以问题的本质不是“用没用无头模式”,而是“浏览器指纹和真人浏览器差异到底有多大”。如果差异大,哪怕你只是规规矩矩打开一个公开页面,也很容易被拦下来。

1.2 UC模式是怎么做到“隐形”的

UC模式的全称是Undetected Chromedriver,最早是社区里的一个开源项目,后来被SeleniumBase整合成了内置能力。它的原理可以粗浅地理解成:

首先,启动一个真实的Chrome浏览器,然后趁浏览器还没完全暴露之前,把chromedriver“嫁接”进去。然后对navigator.webdriver、window.chrome这类常见暴露点做补丁,让页面读到的值和真人浏览器一致。同时通过CDP(Chrome DevTools Protocol)在页面打开之前注入预置脚本,提前覆盖关键属性。

需要注意,UC模式解决的只是“浏览器指纹层”的问题。它不会伪造真人鼠标轨迹,不会帮你模拟键盘输入节奏,更不会保证你绕过所有风控。它做的事情,用一句大白话讲,就是让网站在“你是真人还是机器人”这个判断题上,不再一眼就看出你的破绽。

1.3 Headless模式又是怎么回事

Headless模式是不带界面的浏览器运行方式。浏览器照样执行JS、渲染DOM、发请求,但不需要一个可见窗口。这种模式的最大价值就是省资源、适合大批量跑任务,CI/CD服务器上基本都是它。

老版Headless模式(headless=True)和新版Headless模式(headless2=True,Chrome 112之后)有很大区别。新版无头模式在行为上更接近完整Chrome,UA里的HeadlessChrome标识也少了。在SeleniumBase里,我强烈建议直接用headless2=True,除非你要兼容很老的环境。

2. 环境准备与快速启动

2.1 安装与版本确认

SeleniumBase需要Python 3.7以上,实测用Python 3.10和3.11都是没问题的。安装非常简单:

pip install seleniumbase

它默认会带起selenium、webdriver-manager这些核心依赖。装完建议先确认一下版本:

seleniumbase --version

SeleniumBase启动浏览器时会自动下载匹配的chromedriver,不需要手动配置环境变量,这对新手来说非常友好。如果你在服务器上装,建议把Chrome和chromedriver版本对齐,避免版本不匹配的启动报错。

2.2 两条命令快速跑通两种模式

用SeleniumBase最直观的方式,是直接用pytest跑一个测试类,命令行里加一个开关切模式:

# 默认有头模式 pytest test_demo.py # UC模式 pytest test_demo.py --uc # 新版无头模式 pytest test_demo.py --headless2

test_demo.py的最简内容如下:

# test_demo.py from seleniumbase import BaseCase class DemoTest(BaseCase): def test_example(self): self.open("https://example.com") self.assert_element("h1") self.assert_text("Example Domain", "h1") print("Title:", self.get_title())

这三条命令跑通,你就已经掌握了SeleniumBase的基本用法。接下来要做的就是往里面加自己的业务逻辑。

2.3 用Driver类直接写普通Selenium脚本

如果你的项目不是pytest体系,只想在普通Python脚本里用SeleniumBase驱动浏览器,官方也提供了Driver类:

from seleniumbase import Driver # 等价于“用UC模式启动一个Chrome浏览器实例” driver = Driver(uc=True)

同理,新版无头模式只需要一行:

driver = Driver(headless2=True)

这种封装方式是SeleniumBase的一个亮点——你可以把它当成一个加强版的webdriver.Chrome来用,不用非得把整个测试框架搬过来。后面的实战示例我都会用Driver类来写,兼容性最好,也最贴近真实工作场景。

3. UC模式实战:原理、参数与踩坑记录

3.1 核心参数一览

UC模式相关的常用参数主要有这些:

参数作用
uc=True启动UC模式,老版本里也叫use_undetected=True
headless2=True搭配UC启用新版无头模式,推荐组合
locale="zh-CN"设置浏览器语言区域,避免暴露默认英文环境
agent="自定义UA"覆盖UA字符串
user_data_dir="目录"指定用户数据目录,可保留登录态
proxy="ip:port"使用代理(合规场景,自备代理)
no_sandbox=True关闭沙箱,容器环境常用
disable_gpu=True关闭GPU,服务器环境常用

这里有个容易被忽略的点:UC模式本身已经处理了大量补丁逻辑,你不需要再手动加--disable-blink-features=AutomationControlled之类的参数。画蛇添足,反而可能让组合状态变得不可控。

3.2 一个完整的UC模式示例

# uc_demo.py from seleniumbase import Driver def check_automation_marks(driver): """检查几项典型的自动化暴露特征""" result = driver.execute_script(""" const marks = { webdriver: navigator.webdriver, hasChrome: !!window.chrome, ua: navigator.userAgent, languages: navigator.languages, }; return marks; """) return result def main(): driver = Driver( uc=True, headless2=True, locale="zh-CN", ) try: driver.get("https://example.com") print(driver.title) marks = check_automation_marks(driver) print(marks) finally: driver.quit() if __name__ == "__main__": main()

看到没有,关键就是Driver(uc=True, headless2=True)这一行。剩下的脚本写法和原生Selenium没有任何区别,find_element、execute_script、add_cookie这些API都是兼容的。运行之后,如果navigator.webdriver输出是undefined,说明UC模式正常生效。

3.3 UC模式下几个容易踩的坑

第一个坑:--uc和--headless同时用,有时会起冲突。SeleniumBase的UC模式内部会自己处理无头逻辑,你再显式加--headless反而可能让某些版本行为异常。我实测下来最稳的组合是uc=True加headless2=True。

第二个坑:启动速度慢。UC模式因为要做补丁、预启动、脚本注入这些操作,启动时间比普通模式慢。如果每个测试用例都从零启动,几十个用例累积下来非常吃亏。建议用一个全局复用的Driver实例,或者用pytest的session级fixture管理driver生命周期。

第三个坑:杀毒软件和防火墙拦截。UC模式那套启动方式有时会被安全软件当成“可疑行为”处理。如果你在自己机器上怎么都启动不了,先把安全软件退出试试。这不是开玩笑,我帮同事排查过半天,最后发现是杀毒软件把临时生成的补丁文件隔离了。

第四个坑:不要在UC模式下用driver.close()。UC模式内部管理着多个进程和连接,最好用driver.quit()做完整清理。close()只关闭当前窗口,容易留下僵尸进程,时间久了电脑会越来越卡。

3.4 UC模式的适用边界

必须说清楚,UC模式不是神药。它能解决指纹层面的问题,但如果你操作频率过高、行为轨迹过于机械,该被拦还是会被拦。现在很多风控系统已经把IP信誉、设备指纹、行为频率、鼠标轨迹、打字节奏这些维度都纳入了考察,单靠一个Undetected Chromedriver远不能通吃所有场景。

所以正确的使用姿势应该是:

  • 放慢节奏:两次请求之间加随机延时,不要毫秒级连续操作
  • 固定指纹:不要每次启动都用不同的UA和viewport,固定一套反而更像真人
  • 走正规通道:能做API对接的就优先做API,不要什么都靠浏览器模拟
  • 设置兜底机制:遇到验证码别硬刚,标记出来交给人工处理

4. Headless模式实战:性能优先的测试方案

4.1 Headless模式的价值

Headless模式在自动化测试里是最常见的运行形态。它的核心优势不是“隐身”,而是“轻量”:

  • 没有渲染窗口,内存和CPU占用低,同一台机器能并行跑更多任务
  • 不需要图形桌面环境,适合Docker容器和CI/CD
  • 不受桌面会话影响,远程服务器上随时可以跑

我之前负责过一套回归测试套件,有头模式跑一次要40分钟,切到headless2之后,时间降到25分钟左右,内存占用也省了差不多一半。对需要频繁跑回归的团队来说,这个效率提升非常明显。

4.2 headless和headless2的选择

SeleniumBase里有两个选项:

  • headless=True:走Chrome老版无头模式。UA里会带HeadlessChrome字样,容易被识别,而且部分渲染行为和完整浏览器差异较大
  • headless2=True:走Chrome新版无头模式(要求Chrome 112+)。用单独的headless shell,但行为上更接近完整Chrome,扩展支持和GPU支持也更好

现在这个时间点,除非你要兼容很老的浏览器环境,否则直接无脑用headless2=True。老版无头模式在我个人项目里,基本只用来验证老脚本兼容性了。

4.3 一个Headless模式的实用脚本

# headless_demo.py from seleniumbase import Driver def main(): driver = Driver( headless2=True, disable_gpu=True, # 服务器上无GPU,避免多余开销 window_size=(1920, 1080), ) try: driver.get("https://example.com") print("Title:", driver.title) # 截屏留档 driver.save_screenshot("headless_result.png") print(driver.page_source[:500]) finally: driver.quit() if __name__ == "__main__": main()

Headless模式的代码和普通Selenium脚本几乎一样,核心区别就是初始化Driver时加个headless2=True。如果是在Linux服务器上跑,建议再加no_sandbox=True,很多CI容器默认以root用户启动,不加会报沙箱错误。

4.4 Headless模式下的常见坑

第一个坑:元素不可见导致点击失败。无头模式下有些页面动画、懒加载、滚动事件可能不会正常触发。比如某些表格数据要滚动到底部才加载,无头模式默认屏幕尺寸较小,元素不在视口里就点不了。解决办法是操作前先滚到可视区域:

self.scroll_to("css选择器") self.click("css选择器")

或者干脆用JS点击:

self.click_js("css选择器")

第二个坑:下载文件没反应。无头模式默认下载路径需要显式指定,否则文件不知道存到哪。可以配置download_path参数,或者设置ChromeOptions里的下载目录。如果跑完发现下载目录是空的,多半是这个没配。

第三个坑:截图对比测试误差大。无头模式的字体渲染、图片加载、GPU合成和有头模式有细微差别。如果你在做对比截图测试,建议优先用有头模式做基准,不要在CI里直接用无头截图和有头截图做像素级比对,误报率非常高。

第四个坑:浏览器进程残留。在循环里反复创建Driver而不quit(),用不了多长时间机器就会被拖死。无头模式看似不占窗口,但每个实例都是完整的浏览器进程,资源管理必须做对。

4.5 Headless模式的适用边界

Headless模式适合的任务:

  • 接口型Web自动化冒烟测试
  • 批量页面状态检查(死链、404、页面加载失败)
  • 定时截图留档
  • 公开数据的合规采集
  • 单测框架里的界面冒烟

不适合的任务:

  • 需要人工观察的复杂交互调试
  • 高度依赖GPU渲染的WebGL场景
  • 遇到复杂验证码的少数极端场景,这时候应该考虑UC模式或人工处理

5. 场景化选型:什么时候用UC,什么时候用Headless

5.1 决策依据

选哪种模式,不要凭感觉,要看四个维度:目标网站类型、运行环境、性能要求、稳定性要求。

关键因素UC模式Headless模式
目标网站类型有反自动化检测的站点(且你有操作权限)内部系统或低风控站点
运行环境需要更接近真人的浏览器环境服务器或CI容器
启动性能偏慢,约慢30%到80%快,接近原生
稳定性依赖补丁逻辑,Chrome大版本升级后有波动高,与Chrome版本基本同步
资源占用稍高低
推荐组合uc=True+headless2=Trueheadless2=True+ 合理限速

这里的“有反自动化检测的站点”还是要强调一句:请只在你拥有合法权限的系统和公开页面上操作,不能去搞恶意绕过。测试环境里模拟高风控场景是一回事,对着别人的生产系统发起攻击是另一回事。

5.2 从Headless迁移到UC的平滑路径

如果你已经有一个headless模式的脚本,想小步切换到UC,最安全的做法是:

  1. 先只改初始化逻辑:Driver(headless2=True)改成Driver(uc=True, headless2=True)
  2. 跑通全量用例,观察哪些断言因为页面加载变慢而失败,UC模式启动慢,需要扩大超时
  3. 确认无误后,再把命令行--headless2换成--uc --headless2
  4. 不要同时用两种driver模式跑同一批用例,出现结果差异时很难定位原因

这里有个经验:UC模式对超时非常敏感。以前headless模式2秒就能打开的页面,UC模式可能要4到5秒,因为中间的指纹补丁和脚本注入更耗时。建议把SeleniumBase的页面加载超时调大一点,或者统一用wait_for_element这类显式等待,而不是把睡眠时间写死。

5.3 混合策略:先快后稳

我在实际项目里比较常用的是这套思路:

CI里的基础回归测试用headless2跑,保证速度和稳定性。针对重点页面和关键用户流程,单独抽几条冒烟用例,用UC模式跑一遍,确认没有因为自动化指纹导致功能异常。做合规数据采集时,用headless2做批量扫描,遇到特定异常(验证码、空页面、异常跳转)再降级到UC重新尝试。

这个“先快后稳”的策略,说白了就是让80%的场景走高性能路线,让20%的关键场景走拟真路线。你完全可以根据自己的业务去调整这个比例。

6. 常见问题与排查技巧实录

6.1 UC模式起不来:请对照这五条

  1. Chrome和chromedriver版本不匹配。SeleniumBase会自动下载chromedriver,但如果你本地装了很老的Chrome,可能匹配不上。解决办法是升级Chrome,或指定浏览器版本。
  2. 报错Running as root without --no-sandbox is not supported。容器环境很常见,加no_sandbox=True。
  3. 首次启动卡在下载浏览器二进制的步骤。网络不好时很常见,建议提前配好镜像或手动下载并缓存文件。
  4. 杀毒软件或安全策略拦截。这个最玄学,先退出安全软件再试一次。
  5. 和--headless参数冲突。不要同时用--headless和--uc,优先组合是--uc --headless2。

6.2 Headless模式最常见的四类问题

  1. 元素找不到,大概率是懒加载或滚动没触发,先scroll_to再找。
  2. 页面显示“浏览器版本过低”或JS报错,无头模式的UA、字体、WebGL可能和真实浏览器有差异,尝试换成headless2。
  3. 下载文件没出现在预期目录,确认是否配置了下载路径。
  4. 内存占用持续上涨,检查代码里是否存在临时的Driver没有被quit。

6.3 模式质量自检小工具

在上线自动化脚本前,建议自己先跑一个特征检查:

marks = driver.execute_script(""" return { webdriver: navigator.webdriver, languages: navigator.languages, platform: navigator.platform, deviceMemory: navigator.deviceMemory || null, }; """) print(marks)

如果webdriver不是undefined,说明自动化标记没有隐藏干净,先修基础配置再谈别的。这个小工具我每次排查问题都会用,比任何猜测都管用。

6.4 性能实测记录参考

我自己在同样一台8核16G的Linux服务器上,跑100次打开example.com的简单用例,数据大致是这样的:

  • 有头模式:平均3.2秒,内存占用较高
  • headless2模式:平均1.8秒,内存占用约为有头模式的一半
  • UC模式(配headless2):平均2.9秒,内存占用介于两者之间

这个数据说明一个问题:UC模式确实比纯headless慢,但也没慢到不可接受。在需要拟真的场合,这点性能代价是值得的。具体数值会因网站复杂度和网络环境而变化,但量级关系基本就是这个方向。

7. 最后再聊几点经验

先想清楚你的目标是“快”还是“拟真”。如果是内部系统、低风控页面,用headless2基本不会错;如果页面本身有检测、验证码频繁,也别指望UC一劳永逸。风控是动态的,今天有效的指纹伪装,明天可能就失效了。保持SeleniumBase、Chrome、chromedriver更新到较新版本,比任何花哨技巧都重要。

合规永远比技术优先。做测试也好,做采集也好,只对你拥有合法权限的站点操作,遵守网站的使用条款和robots协议。技术能力是用来解决合理问题的,不是用来突破底线的。

遇到不好解决的问题,先去官方文档翻参数,SeleniumBase的文档里基本能找到答案。实在不行就查GitHub issues,很多坑社区里早有人踩过,别一个人硬扛。

以上就是我从UC模式和Headless模式实战里攒下来的一些干货。如果硬要我总结成一句话,那就是:headless2解决的是成本和稳定性,UC解决的是拟真度,搭配使用,才能走得更远。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询