
1. 项目概述当自动化脚本撞上风控高墙最近在折腾一个Buff饰品市场的自动化交易脚本核心工具就是大家耳熟能详的Selenium。想法很美好自动登录、监控价格、自动下单解放双手实现“睡后收入”。但现实很快给了我一记重拳——脚本运行没几次账号就被限制登录甚至触发了安全验证。我相信很多尝试用Selenium做自动化交易、数据采集的朋友都遇到过类似问题“我的脚本明明模拟了人的操作为什么还是被网站轻易识别出来了”这背后是一场我们自动化脚本与网站风控系统之间持续的攻防战。网站尤其是涉及交易和资产的平台投入了大量资源构建反爬和反自动化机制。它们的目标很明确区分真实的人类用户和机器程序以保障平台安全、公平和数据价值。Selenium虽然强大但它并非隐形斗篷会留下大量可以被检测的“指纹”。这次踩坑实录我就来详细拆解Selenium为什么会被检测以及我们如何从策略到代码细节进行层层优化让脚本变得更“像人”更稳定。2. Selenium被检测的核心原理与常见指纹要解决问题首先得知道问题出在哪。网站检测Selenium或任何自动化工具并非魔法而是基于一系列可观测的特征差异。这些特征就像人的“生物指纹”机器有机器的指纹人类有人类的指纹。2.1 浏览器环境指纹这是最基础的检测层面。Selenium启动的浏览器即使使用了常规的Chrome或Firefox驱动其环境也与正常用户浏览器存在细微差别。1. WebDriver属性这是最广为人知的检测点。通过JavaScript网站可以检查navigator.webdriver属性。在普通浏览器中这个属性是undefined或false而在Selenium控制的浏览器中它会被设置为true。这是很多初级检测脚本的第一道关卡。2. 浏览器插件与特性列表Selenium启动的浏览器通常没有安装任何扩展如AdBlock、密码管理器等。网站可以通过检查navigator.plugins或navigator.mimeTypes的长度和内容来判断。一个“过于干净”的浏览器环境会引起怀疑。此外一些实验性特性或标志位也可能不同。3. 屏幕分辨率与色彩深度自动化脚本有时会使用无头模式Headless或者屏幕分辨率设置得与常见用户设备不符。通过screen.width,screen.height,screen.colorDepth等属性可以获取这些信息。2.2 行为模式指纹即使环境伪装得再好笨拙的行为也会暴露你。这是更高级、更难以规避的检测方式。1. 鼠标与键盘事件的真实性人类操作鼠标时轨迹是带有随机弧度和速度变化的曲线。Selenium的move_to_element()和click()命令通常是瞬间、直线完成的。同样send_keys()输入文本的速度可以是恒定的而人类的输入有快有慢有修正有删除。缺乏真实的MouseMove,MouseOver,KeyDown等事件细节是明显的机器特征。2. 操作时间间隔的规律性机器执行任务的间隔是精确的比如每2秒检查一次元素每500毫秒点击一次。人类的行为间隔具有随机性并且与页面加载时间、阅读时间相关联。固定的、毫秒级精度的等待时间如time.sleep(2)是一个危险信号。3. 页面浏览与滚动模式人类浏览页面会滚动、停顿、快速滑动。脚本可能直接定位到页面底部的元素或者以固定的速度匀速滚动。检查window.scrollY的变化模式可以分析这一点。2.3 网络层与协议指纹这一层相对隐蔽但一些大型平台的风控系统会涉及。1. HTTP头信息Selenium发出的请求其User-Agent虽然可以自定义但其他头信息如Accept-Language,Sec-CH-UA客户端提示等可能不完整或与User-Agent不匹配。一些高级检测会分析头信息的完整性和一致性。2. TLS/SSL指纹不同的客户端浏览器、curl、requests、Selenium驱动的浏览器在建立TLS连接时其加密套件、扩展列表可能存在差异。这是非常底层的特征普通脚本难以修改但确实是一些顶尖风控系统的检测维度。注意面对涉及金融交易的平台其风控系统往往是多层、复合的。它可能不会因为某一项特征就封禁你而是会为你的会话计算一个“风险评分”当多项可疑特征叠加总分超过阈值时才会触发验证或限制。因此我们的优化必须是系统性的。3. 反检测优化策略从“形似”到“神似”了解了检测原理我们就可以有针对性地进行优化。目标不是追求100%不可检测这几乎不可能而是将风险评分降到足够低让脚本能够稳定运行足够长的时间。3.1 基础环境伪装这是第一步旨在消除最明显的自动化指纹。1. 移除WebDriver属性对于Chrome可以通过add_argument添加实验性选项来禁用或覆盖这个属性。这是必须做的一步。from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() # 核心反检测参数 options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) driver webdriver.Chrome(optionsoptions) # 执行CDP命令覆盖navigator.webdriver值 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); })2. 设置合理的User-Agent和视窗使用一个常见的、更新的真实浏览器User-Agent字符串。同时设置一个常见的窗口大小避免使用全屏或过于奇葩的分辨率。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(window-size1920,1080) # 设置一个常见分辨率 # 避免在无头模式下运行除非必要。如果必须用需要更细致的伪装。 # options.add_argument(--headlessnew)3. 启用插件并模拟常见配置可以通过CDP命令来模拟插件存在。虽然不能真正安装插件但可以修改navigator.plugins的返回值使其看起来像安装了常见插件如Chrome PDF Viewer。driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: // 模拟插件 Object.defineProperty(navigator, plugins, { get: () [1, 2, 3, 4, 5], }); Object.defineProperty(navigator, languages, { get: () [zh-CN, zh, en], }); })3.2 高级行为模拟这是让脚本“活”过来的关键也是最考验功力的部分。1. 实现拟人化鼠标移动不要直接使用move_to_element()而是自己实现一个带贝塞尔曲线的移动函数让鼠标移动轨迹带有弧度、加速度和减速度。import random import time from selenium.webdriver.common.action_chains import ActionChains def human_like_move(driver, element): 模拟人类鼠标移动到元素 actions ActionChains(driver) location element.location size element.size # 目标点元素中心附近随机点 target_x location[x] size[width] / 2 random.uniform(-5, 5) target_y location[y] size[height] / 2 random.uniform(-5, 5) # 当前点假设从当前位置或某个起点开始这里简化从(0,0)开始 current_x, current_y 0, 0 # 生成控制点制造曲线 cp1_x current_x (target_x - current_x) * random.uniform(0.2, 0.4) cp1_y current_y (target_y - current_y) * random.uniform(0.3, 0.7) cp2_x current_x (target_x - current_x) * random.uniform(0.6, 0.8) cp2_y current_y (target_y - current_y) * random.uniform(0.3, 0.7) # 贝塞尔曲线插值点简化版实际可用更复杂模型 steps random.randint(30, 60) # 移动步数随机 points [] for i in range(steps): t i / steps # 三次贝塞尔曲线公式 x (1-t)**3*current_x 3*(1-t)**2*t*cp1_x 3*(1-t)*t**2*cp2_x t**3*target_x y (1-t)**3*current_y 3*(1-t)**2*t*cp1_y 3*(1-t)*t**2*cp2_y t**3*target_y points.append((x, y)) # 执行移动每步之间加入随机延迟 for point in points: actions.move_by_offset(point[0] - (actions.w3c_actions._actions[-1][x] if actions.w3c_actions._actions else 0), point[1] - (actions.w3c_actions._actions[-1][y] if actions.w3c_actions._actions else 0)) actions.pause(random.uniform(0.001, 0.005)) # 每步暂停0.001-0.005秒 actions.perform() time.sleep(random.uniform(0.1, 0.3)) # 移动到后稍作停顿 return actions2. 随机化等待与操作间隔彻底抛弃固定的time.sleep()。所有等待时间都应在一个基础值上增加随机扰动。对于等待元素出现使用WebDriverWait但超时时间也可以适当随机化。import random from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def random_sleep(min_sec1.0, max_sec3.0): 随机睡眠模拟人类反应时间 time.sleep(random.uniform(min_sec, max_sec)) # 使用示例点击前等待 random_sleep(0.5, 1.5) element.click() # 对于显式等待可以设置一个随机的基础超时 wait WebDriverWait(driver, timeoutrandom.uniform(10, 15)) element wait.until(EC.presence_of_element_located((By.ID, some-id)))3. 模拟人类输入与滚动输入文本时模仿人类的打字节奏有快有慢偶尔打错并修正。def human_type(element, text): 模拟人类输入文本 element.click() random_sleep(0.1, 0.3) for char in text: element.send_keys(char) # 每个字符输入间隔随机并有一定概率打错 delay random.uniform(0.05, 0.2) if random.random() 0.02: # 2%概率打错一个字符并修正 element.send_keys(Keys.BACK_SPACE) random_sleep(0.1, 0.2) element.send_keys(char) time.sleep(delay)滚动页面时不要一次到底。可以分多次每次滚动随机距离中间有停顿。def human_scroll(driver, scroll_to_bottomFalse): 模拟人类滚动页面 total_height driver.execute_script(return document.body.scrollHeight) viewport_height driver.execute_script(return window.innerHeight) current 0 while current total_height - viewport_height: # 每次滚动一个随机距离约为视口的30%-80% scroll_step random.randint(int(viewport_height * 0.3), int(viewport_height * 0.8)) driver.execute_script(fwindow.scrollBy(0, {scroll_step});) current scroll_step random_sleep(0.5, 2.0) # 滚动后停顿模拟阅读 if not scroll_to_bottom and random.random() 0.3: break # 30%概率中途停止滚动3.3 策略与架构层面的优化单点技术优化之外整体策略更能提升脚本的生存能力。1. 降低请求频率与活跃度不要7x24小时高频率地查询或操作。模拟真实用户的作息在白天活跃夜间长时间休眠。将监控间隔拉长到几分钟甚至更久而不是每秒请求。对于交易脚本频繁的询价和下单是最大的风险点。2. 使用代理IP池这是应对IP级别封锁的核心手段。即使行为伪装得再好一个IP地址发出过多请求也会被限制。你需要一个可靠的代理IP池并在每次会话或每隔一段时间轮换IP。注意代理的质量纯净度、速度、类型住宅代理比数据中心代理更难被识别。3. 账号轮换与冷却如果平台允许且你有多个账号不要把所有压力放在一个账号上。设计一个账号调度系统让每个账号每天只执行有限的操作然后进入“冷却期”。这能极大降低单个账号被风控的概率。4. 模拟完整的用户会话不要一上来就直奔目标如搜索某个商品。可以先访问首页随机点击几个无关的链接浏览一下新闻或公告然后再执行核心操作。这更像一个真实用户的访问路径。4. 实战踩坑Buff自动化交易脚本优化实录结合上面的策略我来分享一下在优化Buff自动化脚本时遇到的具体问题和解决方案。4.1 初期脚本的问题诊断最初的脚本非常简单直白# 问题脚本示例 from selenium import webdriver import time driver webdriver.Chrome() driver.get(https://buff.163.com) time.sleep(3) driver.find_element_by_class_name(login-btn).click() time.sleep(2) # ... 后续登录和操作运行几次后问题接踵而至首次登录可能需要滑块验证原本账号不需要。登录后进行商品搜索和价格监控时很快收到“操作过于频繁请稍后再试”的提示。连续运行几小时后账号被要求重新登录并可能触发手机验证。原因分析固定等待time.sleep(3)这种固定模式极易被识别。无头模式为了方便我最初使用了--headless这本身就是一个高风险特征。直线操作所有查找、点击都是瞬间完成鼠标轨迹异常。高频请求脚本以5秒一次的频率轮询商品价格请求特征明显。单一IP所有请求来自我的家庭IP。4.2 分阶段优化实施第一阶段基础环境伪装与行为随机化首先按照3.1和3.2的内容重构了浏览器启动配置移除了WebDriver特征设置了合理的UA和窗口。然后将所有固定的time.sleep替换为random_sleep函数。为关键操作登录按钮点击、搜索框输入加上了拟人化的鼠标移动和输入函数。效果滑块验证出现的频率有所下降但进行密集操作如连续翻页查看商品时仍然会触发频率限制。第二阶段引入代理IP与请求频率控制我接入了一个付费的住宅代理IP服务。修改脚本使其在每次启动新浏览器实例时通过--proxy-server参数使用不同的代理IP。同时将价格监控的轮询间隔从5秒大幅降低到2-5分钟并且在监控过程中随机插入一些浏览其他页面的“噪音”操作。效果IP被封的问题基本解决“操作频繁”的提示出现次数减少。但长时间运行超过12小时后账号状态依然会变得不稳定。第三阶段模拟完整用户会话与引入冷却期这是提升最大的环节。我重新设计了脚本流程启动会话使用随机代理IP启动伪装好的浏览器。预热浏览不直接登录。先访问Buff首页随机滚动可能点开一两个热门游戏或公告看看停留随机时间。执行登录使用拟人化方式输入账号密码登录。核心操作执行监控或交易任务但严格限制单位时间内的操作次数。例如每小时最多查询30次商品详情每次查询后伴随随机时间的浏览停顿。会话结束核心任务完成后不会立刻关闭浏览器。可能会随机浏览个人库存、市场行情页几分钟然后才退出。账号冷却为每个账号设置每日最大操作次数和最长在线时间。达到阈值后该账号进入冷却队列24小时后再启用。此外我彻底放弃了无头模式让浏览器窗口真实显示出来可以最小化这虽然消耗更多资源但被检测的风险显著降低。效果优化后脚本的稳定运行时间从最初的几小时延长到了数天甚至数周。虽然仍不能保证100%不被检测平台风控也在升级但已完全满足实用需求。4.3 关键代码片段与配置以下是最终版本中几个核心的配置和函数浏览器启动配置Chromedef create_stealth_driver(proxyNone): options Options() # 基础伪装 options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) options.add_argument(--no-sandbox) options.add_argument(--disable-dev-shm-usage) # 禁用自动化栏 prefs { credentials_enable_service: False, profile.password_manager_enabled: False, profile.default_content_setting_values.notifications: 2 } options.add_experimental_option(prefs, prefs) # 设置UA和窗口 user_agents [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..., # ... 准备多个UA轮流使用 ] options.add_argument(fuser-agent{random.choice(user_agents)}) options.add_argument(--window-size1920,1080) # 代理设置 if proxy: options.add_argument(f--proxy-server{proxy}) driver webdriver.Chrome(optionsoptions) # 执行CDP命令覆盖指纹 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, {get: () undefined}); Object.defineProperty(navigator, plugins, { get: () [{ 0: {type: application/x-google-chrome-pdf, description: ...}, length: 1 }], }); }) return driver操作调度与冷却管理简化逻辑class AccountManager: def __init__(self, accounts_config): self.accounts accounts_config # 包含账号、密码、冷却时间戳、今日操作次数 self.proxy_pool [...] # 代理IP列表 def get_available_account(self): 获取一个可用的账号和代理 for acc in self.accounts: if acc[cool_until] time.time() and acc[ops_today] acc[daily_limit]: proxy random.choice(self.proxy_pool) acc[ops_today] 1 return acc, proxy # 所有账号都冷却或达上限等待最久的一个冷却结束 # ... 等待逻辑 return None, None def mark_cooling(self, account, hours24): 标记账号进入冷却 account[cool_until] time.time() hours * 3600 account[ops_today] 05. 常见问题排查与进阶思考即使做了全面优化脚本在运行中仍可能遇到问题。这里记录一些排查思路和进阶可能性。5.1 问题排查清单问题现象可能原因排查与解决思路登录即需验证码/滑块1. 浏览器指纹暴露WebDriver。2. IP信誉差代理IP质量不佳。3. 登录行为异常速度太快。1. 检查CDP脚本是否生效在浏览器控制台输入navigator.webdriver查看。2. 更换代理IP优先使用住宅代理。3. 在输入账号密码前后增加随机等待和误操作模拟。操作频繁被限制1. 单位时间内请求次数过高。2. 操作模式过于规律。3. 账号本身已被标记。1. 大幅降低操作频率增加随机延迟。2. 在操作序列中插入无关的浏览行为。3. 让该账号休息几天或更换账号。页面元素找不到1. 页面结构已更新。2. 脚本执行过快页面未加载完。3. 元素在iframe内。1. 更新元素定位器XPath/CSS Selector。2. 使用显式等待WebDriverWait而非固定等待。3. 使用driver.switch_to.frame()切换到对应iframe。脚本运行一段时间后崩溃1. 浏览器内存泄漏。2. 网络不稳定或代理失效。3. 网页弹出意外弹窗阻塞。1. 定期如每运行2小时重启浏览器实例。2. 增加网络异常重试机制和代理IP健康检查。3. 使用try...except捕获异常并尝试关闭弹窗或刷新页面。5.2 进阶方向与工具当上述常规手段效果有限时可以考虑更深入的方案1. 使用Playwright或Puppeteer替代Selenium这两个现代浏览器自动化库对CDPChrome DevTools Protocol的支持更原生能实现更精细的指纹伪装和行为模拟。例如Playwright可以直接生成带有真实输入事件的脚本模拟更自然。2. 浏览器指纹管理服务有些服务如某些指纹浏览器提供修改Canvas、WebGL、AudioContext等硬件指纹的能力。这些指纹非常独特且难以修改是高级风控的检测点。不过这类服务通常需要付费。3. 逆向分析与接口调用终极解决方案是彻底放弃浏览器自动化转而直接分析网站的网络请求找到核心的数据接口API然后用Python的requests库直接调用。这需要一定的逆向工程能力但一旦成功效率和隐蔽性是最高的。但请注意这可能会违反网站的服务条款风险自担。4. 机器学习行为模拟理论上可以收集大量真实用户的操作数据鼠标移动、点击间隔、滚动模式训练一个模型来驱动脚本使其行为无限接近真人。但这属于研究范畴实现成本极高。核心心得与反爬系统的对抗是一场成本与收益的博弈。我们的目标不是追求绝对隐身而是在可接受的成本时间、金钱、复杂度内获得足够的稳定性和数据。对于Buff这类安全等级高的平台保持低调、模拟真人、降低频率、分散风险是长期运行的关键。永远记住如果你的脚本行为像一个真实而谨慎的用户那么它被盯上的概率就会小很多。