Python Selenium规避浏览器检测:从原理到实战

发布时间:2026/10/18 12:28:50
Python Selenium规避浏览器检测:从原理到实战 前言先说个具体场景我用Selenium写了一个自动登录后台、每日统计报表的脚本跑得好好的但换了个环境部署之后突然就开始报验证码弹窗、页面元素死活点不动、甚至直接被重定向到安全验证页。排查一圈脚本逻辑没变变的是对方能识别出“你不是真人”。这种情况在做数据采集、自动化测试、UI回归验证的时候太常见了。Python搭配Selenium的组合恰好又是各类网站反爬策略重点盯防的对象——它的自动化特征太明显。这篇文章就从我这些年的实操经验出发把“Python用Selenium怎么规避检测”这件事拆开讲。不是给一堆粘贴就能用的魔法代码而是先让你搞懂检测方到底看什么再逐层给出对应的反制思路和配置方法。适合谁看正在写网页自动化脚本、遇到验证码弹窗、被重定向到安全验证页、元素识别失败这类问题的开发者或者刚接触Selenium但想少走弯路的新手。内容聚焦在自动化测试、个人效率工具、公开数据采集这些正经场景讲的是技术原理和合规用法。1. 先搞懂检测方到底在看什么1.1 自动化浏览器的第十一个特征WebDriver属性所有基于Chromium内核的浏览器在开启自动化模式之后都会在页面全局对象window上挂载一个属性navigator.webdriver。正常情况下真人通过Chrome手动访问网页时这个属性的值是undefined而在Selenium驱动的浏览器中它的值是true。大部分网站的反爬脚本只需要一行JS就能完成检测if (navigator.webdriver) { // 判定为自动化浏览器执行拦截 }这是一个最底层、最基础的检测点。但它不是唯一一个。除了navigator.webdriver还有navigator.plugins、navigator.languages、window.chrome、navigator.permissions等一系列指纹点。检测脚本会把这些值组合起来作为一个多维度的判断向量而不是只看单点。1.2 自动化指纹的“拼图”逻辑网站的反爬系统不会只靠一个特征就判定你是机器人而是会把以下信息拼在一起做综合打分检测维度真人浏览器的特征Selenium自动化常见的破绽User-Agent正常的操作系统浏览器版本组合无头模式下的默认UA或存在不匹配关系navigator.webdriverundefinedtrue插件列表有真实插件信息空数组或默认值语言偏好至少1-2种语言可能为空或只有en-US屏幕和视口常见分辨率和缩放比默认800x600或异常尺寸行为节奏鼠标移动有轨迹、点击有间隔事件间隔固定、位置精确如脚本CDP痕迹无会暴露Runtime.enable等协议调用这里要强调一个核心认知反爬系统用的是“综合评分制”单项特征可疑只是加权重不会立刻拦截但当多个特征同时指向自动化工具时风险值瞬间拉满。所以“规避检测”这件事本质上不是抹掉某一个特征而是让整个指纹看起来和真人一致。1.3 不同场景下的检测强度差异还有一个很重要的经验不是所有网站都会用同样的检测强度。电商平台的商品查询、普通博客站点、公开的文章列表页这些通常检测很松而涉及登录、发布、支付、抽奖这类高价值操作的页面检测会严格得多。我自己的实战体会是很多初学Selenium的人之所以觉得“怎么都是验证码”大概率是因为脚本一上来就去模拟登录、频繁操作敏感接口还没到指纹检测那一层就先被风控规则拦住了。2. 第一层反制浏览器启动参数与基础伪装2.1 必设的ChromeOptions参数清单先聊最基础的。用Selenium启动Chrome时很多人只写了webdriver.Chrome()就完事这个裸启动状态被识别几乎是必然。我给自己的脚本设置了一组固定的启动参数每一行都有明确目的from selenium import webdriver options webdriver.ChromeOptions() # 去除“正在受自动化软件控制”的提示条 options.add_experimental_option(excludeSwitches, [enable-automation]) # 屏蔽自动化相关特性如默认的CLI标记 options.add_experimental_option(useAutomationExtension, False) # 设置一个和真实系统匹配的UA options.add_argument(--user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36) # 禁用自动化标志 options.add_argument(--disable-blink-featuresAutomationControlled) # 语言设置不要留空或默认 options.add_argument(--langzh-CN) # 窗口大小要像真实用户的屏幕 options.add_argument(--window-size1920,1080) # 禁用GPU加速在无头模式下减少报错 options.add_argument(--disable-gpu) driver webdriver.Chrome(optionsoptions)这组参数里--disable-blink-featuresAutomationControlled是前几年最核心的一条它会直接去掉Blink引擎中被注入的自动化标记法。但注意近两年主流浏览器的检测逻辑已经升级了单靠这一条不够必须配合其他手段。2.2 无头模式是把双刃剑无头模式--headlessnew跑起来确实方便不弹窗、资源占用低适合在服务器上跑定时任务。但代价是无头模式下的canvas指纹、字体列表、WebGL渲染信息都和真实浏览器有差异专门检测无头模式的脚本很容易把它揪出来。我的建议是如果是需要高稳定性、低被检测概率的任务优先使用有头模式搭配虚拟显示器比如在Linux服务器上用Xvfb来跑如果场景确实需要无头模式那就要在指纹层面做额外补偿后面第3节会展开。2.3 别忘了User-Agent和版本匹配UA的设置不能拍脑袋。你设置的UA版本一定要和实际驱动起来的浏览器版本大致对应。比如你安装的是Chrome 120却把UA写成Chrome 96这种新旧混乱本身就是破绽。我之前遇到过一个小伙伴Chrome已经升到122了UA还停留在110被对方的风控系统一眼识别出来。这里有个更隐蔽的细节移动端UA和桌面端UA的服务器响应完全不同。如果脚本拿的是移动端UA但视口尺寸又是桌面尺寸这个矛盾也会被记录。所以建议UA、视口宽高、操作系统的指纹三者保持一致。3. 第二层反制清除JavaScript指纹痕迹3.1 执行CDP命令覆写WebDriver属性就算配置了--disable-blink-featuresAutomationControlled也无法保证100%隐藏自动化痕迹。更稳妥的做法是在页面加载前通过Chrome DevTools ProtocolCDP直接覆写navigator.webdriver的值。# 在浏览器实例创建后、访问任意页面之前执行CDP命令 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); })这段代码的含义是每当浏览器加载一个新文档时在执行页面自身的任何脚本之前先把navigator.webdriver这个属性的getter重写为返回undefined。这个时序非常关键——如果是在页面加载完成后才去改对方的检测脚本可能已经读取并记录了原始值改了也来不及。3.2 补齐插件、语言、权限等关联指纹只处理webdriver属性还不够因为对方还会查关联指纹。下面这几段JS可以一起注入Object.defineProperty(navigator, plugins, { get: () [1, 2, 3, 4, 5] }); Object.defineProperty(navigator, languages, { get: () [zh-CN, zh, en] }); const originalQuery window.navigator.permissions.query; window.navigator.permissions.query (parameters) ( parameters.name notifications ? Promise.resolve({ state: Notification.permission }) : originalQuery(parameters) );plugins返回一个假数组languages模拟真实用户的语言选项permissions绕过“通知权限是默认值”这类自动化特征。这些脚本加到一起能在指纹层把Selenium和真实浏览器的距离拉近一大截。3.3 这是否足够通过强检测说实话不够。这些手段能应对大多数常规反爬策略但对于用上了商业级风控系统的平台仍然存在被识别出的可能。原因在于指纹检测的上游还有行为层、IP层、设备层等更重的因素单靠JS注入只是“及格”不是“满分”。这也是为什么我强调完整的规避方案必须是从上到下逐层配置的任何一层偷懒都会让整体失效。4. 第三层反制行为模拟让操作节奏像真人4.1 千篇一律的停顿是最大的破绽很多Selenium脚本的写法就是find_element().click()一气呵成中间没有任何停顿事件间隔固定鼠标坐标永远精确到元素中心。这种“机械感”太明显了。真人的操作一定带随机性移动鼠标有误差、点击按钮之前会犹豫、滚动页面会时快时慢。如果你用Selenium自带的ActionChains去模拟必须加入随机化的延时。这个延时不是简单写个sleep(2)就完事而是要在关键节点各自加上不同分布的随机时间。import random import time BASE_DELAY 0.5 RANDOM_DELAY_RANGE (0.3, 1.5) def human_pause(): time.sleep(BASE_DELAY random.uniform(*RANDOM_DELAY_RANGE))在真实使用中我一般会在页面加载后多停1-2秒让所有异步资源都渲染完再开始操作每次点击按钮之间加上0.3到1.2秒的浮动间隔滚动页面时也不是一次性滚到底而是分几次滚动每次滚动幅值都有随机变化。4.2 用鼠标轨迹而不是瞬间跳变ActionChains的move_by_offset或move_to_element默认是瞬移这个行为在带轨迹记录的系统里会被标记为异常。要尽量让鼠标的移动像“人”而不是“点”可以模拟多段贝塞尔曲线插值移动。下面是一个简化但可用的轨迹生成思路def human_mouse_path(start, end, steps15): 生成从start到end的带噪声的插值路径点 x0, y0 start x1, y1 end points [] for i in range(steps): t i / steps # 在直线插值基础上加入正弦抖动 x x0 (x1 - x0) * t random.uniform(-2, 2) y y0 (y1 - y0) * t random.uniform(-2, 2) points.append((x, y)) return points然后在ActionChains里逐段执行移动每段之间加20-50ms的微小延时。这样生成的轨迹会有自然的抖动和弧度不会是一条完美的直线。4.3 滚动和输入节奏都被记录的行为痕迹前端埋点能记录的信息比你想的多得多滚动事件触发频率、单次滚动距离、从鼠标move到click之间的潜伏期、输入框里相邻两个字符的输入间隔。这些数据汇总后可以和真人行为模型做比对。输入框也需要注意。用send_keys一次性把整段内容怼进去和真人一个个字符敲进去是有区别的。可以用循环逐字符输入并且每个字符的间隔做随机化同时模拟偶尔打错字再删除重打的操作。虽然会拖慢脚本速度但在高价值操作节点上值得付出这点性能代价。4.4 避免固定模式随机化还没完就算你加了随机延时如果策略是“每次都延1秒”那也是固定模式。我把随机化的颗粒度做得更细每次会话启动时随机选定一套延时分布参数每次任务执行时在几个预设的行为模板快热型、平稳型、谨慎型中随机选一个。这样能避免多维度的指纹统计被聚成一类。5. 进阶实战从示例代码到稳定运行的完整操作5.1 完整代码骨架示例把前面几节的内容整合成一个完整示例。这个示例不针对任何具体网站就是一个可复用的Selenium启动和操作模板import random import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.options import Options def create_stealth_driver(): options Options() # 基础伪装参数 options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) options.add_argument(--langzh-CN) options.add_argument(--window-size1920,1080) # 选择合适版本的UA options.add_argument( --user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 ) driver webdriver.Chrome(optionsoptions) # 载入前覆写JS指纹属性 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, {get: () undefined}); Object.defineProperty(navigator, plugins, { get: () [1, 2, 3, 4, 5] }); Object.defineProperty(navigator, languages, { get: () [zh-CN, zh] }); }) return driver def safe_find_and_click(driver, by, selector): human_pause() element WebDriverWait(driver, 10).until( EC.element_to_be_clickable((by, selector)) ) # 获取元素中心点后用轨迹移动过去点击 from selenium.webdriver.common.action_chains import ActionChains actions ActionChains(driver) actions.move_to_element_with_offset(element, random.uniform(-3, 3), random.uniform(-3, 3)) actions.pause(random.uniform(0.1, 0.4)) actions.click() actions.perform()5.2 逐步验证用隐形测试页检查是否被识别完成配置后第一步不是直接上目标网站而是先用一些指纹检查页面来验证当前配置有没有漏网之鱼。常见做法是打开一个公开的浏览器指纹检测站点观察几项关键指标UA是否和真实浏览器一致WebDriver属性是否显示False或undefined浏览器语言列表是否包含中文Canvas指纹是否生成正常屏幕分辨率和可用视口是否匹配是否有权限相关的异常提示。这一步非常关键能帮你在正式战斗前发现配置问题而不是等到目标网站上被拦了才一个个排查。5.3 从桌面脚本到定时任务的迁移注意点很多人的自动化脚本最终要部署到服务器上定时跑。这里有一个常见的坑服务器上通常没有图形界面直接启动Chrome会失败或进入无头模式。无头模式本身不是问题但和无头模式伴生的指纹异常需要额外处理。我通常的做法是在服务器上安装虚拟显示框架让Chrome以有头模式运行在虚拟屏幕上这样能最大限度保留真实浏览器的指纹特征。具体安装命令不展开但思路是让系统里存在一个X serverChrome照常以GUI模式启动只是这个GUI不显示在物理屏幕上。5.4 定期维护浏览器版本和参数随环境变化还有一个被很多人忽略的事情浏览器的自动更新会导致Selenium驱动版本不匹配。今天写得没问题的脚本过半个月可能就报“session not created”的错误。所以如果脚本是长周期在跑的建议显式锁版本或者写一个检测驱动版本和浏览器版本是否匹配的启动前检查避免上线跑着跑着崩掉。6. 常见问题与排查实录6.1 问题速查表现象可能原因解决方向页面提示“浏览器环境异常”指纹整体不匹配检查UA、视口、语言是否一致一直点验证码/滑块行为特征异常增加随机延时避免固定点击模式元素定位到了但点击无效被风控拦截或元素被遮挡先判断页面是否弹出了安全验证层登录后立刻掉线Cookie/设备指纹被关联减少操作频率补充指纹注入无头模式频繁被识别无头指纹特征明显改用虚拟显示器有头模式打开页面瞬间被重定向IP或UA被列入黑名单检查出口IP质量调整UA版本6.2 我踩过的一些坑第一个坑是过度配置。一开始为了求稳我把所有能搜到的反检测参数全加上结果某些参数之间互相冲突页面加载反而变慢更容易被识别。后来我总结出一个原则能用官方API解决的就不要用hack方式每加一层配置就多一个潜在冲突点配置越少越容易维护。第二个坑是只改指纹不改行为。有一次脚本在指纹检测页上全绿但一上目标站点就被拦。后来才发现是脚本运行节奏太机械所有点击间隔几乎一样行为模型分分钟暴露。这印证了一张完整的规避体系里指纹和行为必须同时到位。第三个坑是盲目升级浏览器版本。Chrome升级之后某些旧的启动参数就失效了甚至引入了新的反自动化工件。建议在升级前先在一个独立的测试环境里跑一遍验证脚本确认没问题再切到生产环境。6.3 检测与规避的动态博弈写到这里我想强调一个长期视角Selenium规避检测不是一锤子买卖而是持续对抗的过程。反爬技术会不断升级今天有效的参数组合三个月后可能就失效了。保持学习和定期验证才是稳定运行的根本。从个人开发者的角度来说我更倾向把这套能力用在正常业务上自动化报表、UI回归、竞品公开数据监测都完全够用。只要不拿它去恶意攻击、绕过认证、刷量刷单技术本身是干干净净的工具。7. 个人一点补充的心得7.1 如果被检测了先别急着加参数很多朋友一遇到验证码弹窗第一反应是去网上找“加强版参数”把别人发的所有代码不管三七二十一全粘进去。这种做法往往适得其反。我的建议是被检测了先冷静下来做诊断打开指纹检测页逐项看确定到底哪一项异常再针对性地修复。就像看病一样不确诊就乱吃药只会加重病情。7.2 理性看待“规避检测”的边界做自动化开发久了你会发现真正决定成败的不是某项单一技术而是对整个系统的理解深度你知道对方在采集什么数据、用什么样的模型去做判断、哪些信号是强信号哪些是弱信号。有了这个认知写脚本的时候心里就有底。7.3 最后分享一个小技巧在所有配置都做完之后建议每次正式跑任务前先做一次“试跑”开着浏览器的开发者工具观察Console和Network面板里有没有异常报错和可疑请求。有时候对方已经在页面上埋了检测脚本只是没直接弹窗而是在后台记录你的行为。试跑能看到这些预警帮你提前发现问题。另外如果你的脚本需要长时间在服务器上运行强烈建议配上失败自动重试和异常通知别让一个夜间任务卡死在验证码页面上一宿这是熬夜排错的人最真实的教训。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询