
简介这是一套基于 Selenium 与 Python 实现的华为商城抢货工具项目源码面向计算机相关专业的在校学生、教师及企业开发者可用于毕业设计、课程设计、项目立项演示或自动化测试学习进阶。资源包共 22 个文件约 4.63MB以 js 脚本、json 配置、yml 持续集成文件为主另含 md 说明文档、exe 驱动及 eslint、prettier 等工程化配置覆盖控制器、服务、视图、路由等模块目录结构清晰便于按功能检索与二次开发。项目已通过测试运行功能完整并附有详细文档与项目授权码方便读者快速理解抢购流程中的页面元素定位、请求调度与异常处理思路。目前已有 60 人学习下载适合在此基础上修改扩展实现其他自动化功能或直接用于毕设、课设与作业场景。1. 华为商城抢货工具从 Selenium 到秒级下单这套源码到底能跑多远华为商城每次 Mate 系列开售页面按钮从灰变红的时间窗口通常只有 3 到 8 秒手动点击基本靠运气。基于 Selenium 的华为商城抢货工具核心思路就是用浏览器自动化把「登录 → 进入商品页 → 等待开售 → 点击立即购买 → 提交订单」这条链路全部脚本化把人的反应时间压缩到毫秒级轮询。这套资料齐全的源码项目适合两类人一是想学 Selenium 实战但不想只写 demo 的 Python 开发者二是对抢购逻辑好奇、想自己搭一套可控工具的技术爱好者。它解决的不是「必中」问题而是把重复操作交给程序、把决策留给人。需要先说明任何抢购工具都受平台风控、库存和网络延迟制约源码能帮你跑通流程但不承诺结果。2. Selenium 抢货工具的架构拆解与选型理由2.1 为什么用 Selenium 而不是纯 requests抢购场景下纯 requests 方案需要逆向登录加密、订单签名、风控 token华为商城的登录链路涉及多层加密参数逆向成本极高且容易随版本失效。Selenium 直接驱动真实浏览器复用浏览器自身的 JS 执行环境和 Cookie 管理登录环节可以半自动完成——手动扫码或输入验证码后脚本接管后续操作。这是「能用」和「好用」之间的平衡点。常见做法是 Chrome ChromeDriver 组合配合 undetected-chromedriver 降低被识别概率。选型时要注意Selenium 4.x 之后 Driver 管理已经内置不再需要手动下载 chromedriver.exe 放到 PATHwebdriver.Chrome()会自动匹配版本。如果你还在用 Selenium 3.x 的executable_path写法建议升级否则 Chrome 自动更新后驱动版本不匹配会直接报SessionNotCreatedException。from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() # 关闭自动化特征提示降低被检测概率 options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) # 保持浏览器打开方便观察抢购过程 options.add_experimental_option(detach, True) # 设置窗口大小避免移动端页面布局干扰元素定位 options.add_argument(--window-size1920,1080) driver webdriver.Chrome(optionsoptions) # 覆盖 navigator.webdriver 属性这是最基础的指纹隐藏 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, {get: () undefined}) })这段代码做了三件事关闭自动化提示条、设置固定窗口尺寸、通过 CDP 命令覆盖navigator.webdriver。参数说明excludeSwitches去掉「Chrome 正受到自动测试软件控制」的提示detach设为 True 后脚本结束浏览器不关闭方便你检查页面状态CDP 那行是关键很多风控脚本第一件事就是检查navigator.webdriver是否为 true。2.2 页面元素定位与等待策略华为商城商品页的「立即购买」按钮在不同状态下 DOM 结构会变化未开售时是 disabled 的 div开售瞬间变成可点击的 button。用find_element(By.XPATH, ...)硬找很容易翻车因为 class 名可能带哈希后缀。稳妥做法是用文本内容定位配合显式等待轮询。from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time def wait_for_buy_button(driver, timeout30, poll0.05): 轮询等待立即购买按钮可点击poll 控制轮询间隔 end_time time.time() timeout while time.time() end_time: try: # 用文本匹配避免 class 哈希变化导致定位失效 btn driver.find_element(By.XPATH, //*[contains(text(),立即购买)]) if btn.is_enabled() and btn.is_displayed(): return btn except Exception: pass time.sleep(poll) # 50ms 轮询兼顾灵敏度和 CPU 占用 raise TimeoutError(等待立即购买按钮超时) btn wait_for_buy_button(driver) btn.click()这里没有用WebDriverWait的element_to_be_clickable原因是它的默认轮询间隔是 500ms对抢购来说太慢。自己写循环把 poll 降到 0.05 秒能在按钮变色的瞬间就捕获到。参数说明timeout30是最大等待秒数poll0.05是轮询间隔实际抢购中可以根据 CPU 占用调整到 0.02 到 0.1 之间。注意is_enabled()对 div 元素永远返回 True所以如果按钮是 div 模拟的要改用get_attribute(class)判断是否包含 disabled 关键字。2.3 登录态保持与 Cookie 复用每次抢购都重新登录不现实验证码和短信验证会消耗宝贵时间。正确做法是第一次手动登录后把 Cookie 序列化保存到本地后续启动直接注入。import pickle import os COOKIE_FILE huawei_cookies.pkl def save_cookies(driver): with open(COOKIE_FILE, wb) as f: pickle.dump(driver.get_cookies(), f) def load_cookies(driver, domainhttps://www.vmall.com): if not os.path.exists(COOKIE_FILE): return False driver.get(domain) # 必须先访问域名否则 add_cookie 会报错 with open(COOKIE_FILE, rb) as f: cookies pickle.load(f) for cookie in cookies: # 去掉 expiry 字段避免时间格式兼容问题 cookie.pop(expiry, None) try: driver.add_cookie(cookie) except Exception as e: print(f注入 cookie 失败: {cookie.get(name)}, {e}) driver.refresh() return True逻辑说明add_cookie要求当前页面域名与 cookie 域匹配所以必须先driver.get到目标域名再注入。参数说明cookie.pop(expiry, None)是血泪经验Selenium 对 expiry 的时间戳格式敏感带这个字段经常报InvalidCookieDomainException去掉后反而稳定。注入完成后必须refresh()让 cookie 生效。如果刷新后仍是未登录状态说明关键 cookie 是 HttpOnly 或 SameSite 限制需要在浏览器启动参数里加--disable-web-security配合用户数据目录但这样会降低安全性仅建议在本地测试环境使用。3. 从源码到可运行环境搭建与核心流程实现3.1 依赖安装与目录结构拿到源码压缩包后先别急着pip install -r requirements.txt。很多项目把依赖写得很粗实际跑起来会缺包。我一般会先看 import 语句再手动装核心依赖。# 核心依赖版本尽量用较新的稳定版 pip install selenium4.15.0 pip install undetected-chromedriver3.5.0 pip install requests2.31.0 pip install pillow10.0.0 # 验证安装 python -c import selenium; print(selenium.__version__)目录结构通常长这样main.py是入口config.py放账号和商品链接utils/下是浏览器封装和日志模块cookies/存登录态。如果源码里没有config.py自己建一个把敏感信息从主逻辑里抽出来方便切换商品和账号。# config.py 示例 TARGET_URL https://www.vmall.com/product/xxxx.html # 替换为目标商品链接 BUY_TIME 2024-11-11 10:08:00 # 开售时间用于定时启动 POLL_INTERVAL 0.05 # 轮询间隔秒 MAX_RETRY 3 # 提交订单最大重试次数参数说明BUY_TIME用于脚本内部计算提前量一般提前 5 到 10 秒启动浏览器并进入商品页POLL_INTERVAL不要低于 0.02否则浏览器主线程会被 Python 循环拖慢反而降低响应速度MAX_RETRY控制提交订单失败后的重试次数设太大容易触发风控。3.2 定时启动与开售瞬间的点击逻辑抢购的关键不是点击本身而是点击的时机。太早按钮不可点太晚库存没了。常见做法是用系统时间对齐在开售前 2 秒开始高频轮询。import datetime import time def wait_until(target_time_str): 阻塞等待到目标时间提前 2 秒返回用于预热 target datetime.datetime.strptime(target_time_str, %Y-%m-%d %H:%M:%S) pre_target target - datetime.timedelta(seconds2) while datetime.datetime.now() pre_target: time.sleep(0.1) return True # 主流程 wait_until(BUY_TIME) driver.get(TARGET_URL) load_cookies(driver) driver.refresh() # 进入商品页后立即开始轮询 try: btn wait_for_buy_button(driver, timeout15, pollPOLL_INTERVAL) btn.click() # 点击后等待订单确认页加载 WebDriverWait(driver, 5).until( EC.presence_of_element_located((By.XPATH, //*[contains(text(),提交订单)])) ) submit driver.find_element(By.XPATH, //*[contains(text(),提交订单)]) submit.click() print(订单已提交请手动完成支付) except TimeoutError: print(未抢到按钮未出现或库存不足)逻辑说明wait_until提前 2 秒返回留出浏览器加载和 cookie 注入的时间。点击「立即购买」后页面会跳转到订单确认页这里用WebDriverWait等待「提交订单」按钮出现再执行第二次点击。参数说明timeout15是等待按钮出现的最大秒数如果 15 秒内按钮都没出现基本可以放弃WebDriverWait(driver, 5)是订单页加载等待网络慢时可以调到 8 到 10 秒。注意提交订单后不要自动支付涉及资金安全的步骤必须人工确认。3.3 日志与失败截图排查问题的后悔药抢购失败时最怕不知道卡在哪一步。加一套日志和截图机制相当于给脚本装了黑匣子。import logging from datetime import datetime logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s, handlers[ logging.FileHandler(grab.log, encodingutf-8), logging.StreamHandler() ] ) def snapshot(driver, tagerror): 失败时截图文件名带时间戳 ts datetime.now().strftime(%Y%m%d_%H%M%S) path fscreenshot_{tag}_{ts}.png driver.save_screenshot(path) logging.info(f截图已保存: {path}) return path # 在异常捕获中调用 try: btn wait_for_buy_button(driver, timeout15) btn.click() except Exception as e: logging.error(f抢购流程异常: {e}) snapshot(driver, buy_fail)参数说明日志同时输出到文件和控制台encodingutf-8避免中文乱码截图文件名带时间戳方便对比不同轮次的状态。如果截图显示页面是登录页说明 cookie 失效如果显示「已售罄」说明点击时机晚了如果显示验证码说明触发了风控需要降低轮询频率或更换网络环境。4. 避坑指南Selenium 抢华为商城最容易翻车的 5 个点4.1 现象脚本启动后浏览器闪退报 SessionNotCreatedException原因Chrome 自动更新到新版本但 chromedriver 还是旧版两者不匹配。Selenium 4.x 虽然内置了 Driver 管理但如果本地 PATH 里有旧版驱动会优先使用旧版。解决删掉项目目录和 PATH 里的 chromedriver.exe让 Selenium 自己下载匹配版本。如果公司网络限制下载手动去 Chrome for Testing 页面下载对应版本放到~/.cache/selenium/chromedriver/下。验证命令python -c from selenium import webdriver; dwebdriver.Chrome(); print(d.capabilities[browserVersion])。4.2 现象按钮明明可见但 click() 报 ElementNotInteractableException原因华为商城部分按钮被透明遮罩层覆盖或者元素在可视区域外Selenium 的 click 要求元素可交互。解决先滚动到元素位置再用 JS 点击兜底。代码driver.execute_script(arguments[0].scrollIntoView({block:center});, btn)然后driver.execute_script(arguments[0].click();, btn)。注意 JS 点击不会触发某些前端框架的事件监听如果无效改用ActionChains(driver).move_to_element(btn).click().perform()。4.3 现象轮询间隔设成 0.01 秒后页面反而卡死原因Python 的time.sleep(0.01)在 Windows 上实际精度约 15ms而且高频 find_element 会不断向浏览器发送 CDP 命令浏览器主线程忙于响应查询没时间渲染页面状态变化。解决轮询间隔不要低于 0.05 秒Windows 下建议 0.08 到 0.1 秒。更好的做法是用WebDriverWait配合自定义polling参数或者用driver.execute_script在页面内注入 MutationObserver 监听按钮状态变化把轮询逻辑放到浏览器端。4.4 现象Cookie 注入成功但刷新后仍是未登录原因华为商城的登录态不仅依赖 Cookie还依赖 localStorage 里的 token。只注入 Cookie 不够。解决在保存 Cookie 的同时用driver.execute_script(return window.localStorage.getItem(token))把关键 localStorage 项也存下来加载时先注入 Cookie再用execute_script写回 localStorage最后刷新。注意 localStorage 是按域名隔离的必须先driver.get到目标域名再操作。4.5 现象提交订单时弹出滑块验证脚本无法继续原因高频操作触发了风控平台判定为机器行为。解决降低轮询频率增加随机延迟比如在每次点击前time.sleep(random.uniform(0.1, 0.3))。如果已经弹出滑块Selenium 处理滑块的成功率很低建议接入打码平台或改用半自动模式——脚本停在提交订单前人工完成滑块后继续。血泪经验不要试图用 Selenium 模拟鼠标轨迹过滑块现在的风控会检测加速度和轨迹曲率脚本模拟的轨迹太完美反而容易被识别。5. 进阶技巧用 CDP 监听网络请求把抢购响应压到极限前面讲的都是 DOM 轮询本质上是「等页面变了再反应」。更激进的做法是用 Chrome DevTools Protocol 直接监听网络请求在商品库存接口返回的瞬间就触发点击比 DOM 轮询快 100 到 300 毫秒。这个时间差在秒杀场景里可能就是抢到和抢不到的区别。from selenium import webdriver import json import threading options webdriver.ChromeOptions() options.add_experimental_option(detach, True) driver webdriver.Chrome(optionsoptions) # 启用网络监听 driver.execute_cdp_cmd(Network.enable, {}) # 用后台线程读取 CDP 事件 def listen_network(): while True: try: # 获取性能日志中的网络事件 logs driver.get_log(performance) for entry in logs: msg json.loads(entry[message])[message] if msg[method] Network.responseReceived: url msg[params][response][url] # 替换为实际的库存查询接口关键字 if inventory in url or stock in url: print(f库存接口响应: {url}) # 在这里触发点击逻辑 return except Exception: pass # 启动监听线程 t threading.Thread(targetlisten_network, daemonTrue) t.start()逻辑说明Network.enable开启 CDP 的网络域driver.get_log(performance)读取浏览器性能日志从中过滤出Network.responseReceived事件。参数说明inventory和stock是库存接口的 URL 关键字需要你打开开发者工具在 Network 面板里找到实际的接口路径再替换。注意get_log会清空日志缓冲区高频调用可能丢事件建议用Network.webSocketFrameReceived或直接上selenium-wire库做代理拦截。另一个实用技巧是预热浏览器实例。开售前 30 秒启动两个 Chrome 实例一个用于监控库存接口一个用于执行点击两者通过本地 socket 通信。这样点击实例的页面始终处于商品页不需要在开售瞬间做页面跳转省掉 1 到 2 秒的加载时间。我自己的习惯是提前 1 分钟把所有实例准备好开售前 10 秒开始 CDP 监听接口一响就点点完立即提交全程不碰鼠标。这套流程跑通之后剩下的就交给网络延迟和库存了。希望帮到你。本文还有配套的精品资源点击获取