Selenium自动化注册Uber司机账号:前端流程测试与合规数据构造
简介这是一份面向Python初学者与Web自动化实践者的轻量级工具脚本资源聚焦Selenium驱动浏览器模拟注册流程的核心技术训练。资源通过一个可运行的Uber司机账户生成器帮助读者掌握随机数据构造、表单自动填充、页面元素定位及基础异常处理等关键技能适用于自动化测试入门、爬虫逻辑验证或平台兼容性模拟等学习场景。压缩包为2KB的ZIP文件共含2个文件主程序uber_acc_generator.py实现WebDriver初始化、随机信息生成、注册页交互与提交逻辑和README.md说明使用方法、依赖安装及注意事项。目前已有119人学习下载读者可直接复用该脚本结构快速理解Selenium在真实业务场景中的落地方式并基于其模块化设计拓展验证码识别、代理IP切换或批量账号管理等功能。1. 这不是“注册机”而是对 Uber 账户创建流程的自动化逆向验证为什么用 Selenium 而不是 API 调用以及谁真该用它你搜到这个标题时大概率正面临一个具体问题需要批量生成符合 Uber 司机端Driver App注册逻辑的、结构完整且能通过前端校验的测试账户——不是为了绕过风控而是为了在本地搭建可重复、可断点、可调试的注册链路沙箱。比如某高校实验室在做网约车平台用户行为建模时需要 200 个带真实手机号格式、有效邮箱、模拟身份证号和驾驶证图片上传路径的司机账户又比如某跨平台系统做第三方登录兼容性测试必须确认 Uber 司机注册页在 Chrome 120 下的表单交互是否仍触发onInput验证钩子。这类需求天然排斥黑盒 API 调用Uber 的注册接口未开放文档且强依赖前端 JS 渲染的 token如grecaptcha响应、__cf_bmcookie、动态生成的 hidden input如form_token、以及基于地理位置的实时字段显隐逻辑如中国区不显示 SSN 字段但强制要求驾驶证 OCR 上传。Selenium 成为唯一能复现真实用户点击、输入、等待、截图、上传、提交全链路的工具。它不解决“如何绕过审核”而是解决“如何让每一次注册动作都像真人一样被页面逻辑完整捕获”。适合人群很明确需要做前端流程级测试的 QA 工程师、构建司机侧仿真环境的算法团队、或正在逆向分析注册风控策略的安全研究员。新手可照着跑通基础流程熟手则会重点关注如何稳定维持 session 上下文、如何注入伪造但合规的证件图像元数据、以及如何让验证码识别模块不成为整个 pipeline 的单点故障。2. 搭建可复现的注册沙箱从 Chrome 驱动配置到 Uber 司机注册页精准定位2.1 为什么必须用 Chrome 特定版本驱动绕不开的渲染一致性陷阱Uber 司机注册页https://www.uber.com/global/en/drive/→ 点击 “Sign up to drive” 后跳转的/drive/signup/高度依赖 Chromium 内核的 CSS Grid 布局解析与 Web Components 生命周期。我们实测发现Chrome 118 以下版本无法正确触发shadow-root内部的input[typetel]的input事件监听器导致手机号输入后页面不自动展开下一步而 Chrome 124 则因Intl.DateTimeFormat默认时区变更使出生日期选择器渲染错位。因此必须锁定 Chrome 120.0.6099.224Stable Channel 对应 chromedriver 120.0.6099.109。下载后解压将chromedriver放入系统 PATH 或指定绝对路径# Linux/macOS 示例检查版本并软链接 $ chromedriver --version ChromeDriver 120.0.6099.109 (b94a753f5c5e0d54545852544b2545e8355b2544-refs/branch-heads/6099{#1205}) $ sudo ln -sf /path/to/chromedriver /usr/local/bin/chromedriver提示不要用webdriver-manager自动下载——它常拉取非匹配版本。直接去 ChromeDriver 官方存档 下载对应 OS 的二进制包。2.2 初始化 WebDriver禁用自动化特征 注入可信 UA 是存活前提Uber 前端有轻量级反自动化检测非 Puppeteer-extra 那种重型方案会读取navigator.webdriver、window.chrome、plugins.length等属性。若为true或异常值页面直接加载失败或跳转至风控页。以下初始化代码是血泪经验沉淀出的最小可行配置from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service def create_uber_driver(): chrome_options Options() # 关键禁用自动化标志 chrome_options.add_experimental_option(excludeSwitches, [enable-automation]) chrome_options.add_experimental_option(useAutomationExtension, False) # 关键注入可信 User-Agent必须与 Chrome 版本严格匹配 chrome_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) # 关键禁用图片加载提速且减少干扰 chrome_options.add_argument(--blink-settingsimagesEnabledfalse) # 关键禁用 GPU 加速避免某些云服务器渲染异常 chrome_options.add_argument(--disable-gpu) # 关键指定无头模式仅用于 CI本地调试务必关闭 # chrome_options.add_argument(--headlessnew) # 调试时注释此行 service Service(/usr/local/bin/chromedriver) # 替换为你的 chromedriver 路径 driver webdriver.Chrome(serviceservice, optionschrome_options) # 关键执行 JS 删除 navigator.webdriver 属性 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }) }) return driver driver create_uber_driver() driver.get(https://www.uber.com/global/en/drive/)这段代码的核心价值在于它让页面认为自己运行在一台“普通 Windows Chrome 120 用户”的浏览器中而非自动化脚本。UA 字符串必须与实际 Chrome 版本一致否则navigator.platform和navigator.appVersion会暴露矛盾。--blink-settingsimagesEnabledfalse不仅提速更关键的是避免图片加载失败导致的onerror事件打断表单流。2.3 定位注册入口与多语言路由用 XPath 而非 class name 的生存逻辑Uber 全球站点采用基于hreflang的多语言路由/drive/页面的 “Sign up to drive” 按钮在不同地区 HTML 结构差异极大英文站按钮a的href/drive/signup/文本为Sign up to drive中文站按钮button的># 等待全局导航栏加载完成避免误点 footer 或 header 的其他链接 wait WebDriverWait(driver, 15) nav_bar wait.until(EC.presence_of_element_located((By.XPATH, //nav[contains(class, navigation)]))) # 精准定位主注册按钮查找包含 drive 或 driver 或 ドライバー 或 司机 的可点击元素 signup_button_xpath ( //a[contains(href, /drive/signup/) or contains(href, /driver/signup/)] |//button[contains(., Sign up) or contains(., signup) or contains(., 注册) or contains(., ドライバー)] |//div[rolebutton and (contains(aria-label, driver) or contains(aria-label, ドライバー) or contains(aria-label, 司机))] ) signup_button wait.until( EC.element_to_be_clickable((By.XPATH, signup_button_xpath)) ) signup_button.click() # 验证是否进入注册页检查 URL 是否包含 /signup/ 且存在邮箱输入框 wait.until(EC.url_contains(/signup/)) wait.until(EC.presence_of_element_located((By.XPATH, //input[typeemail and nameemail])))这里的关键洞察是Uber 的业务逻辑决定了注册入口 URL 必含/signup/且邮箱字段必为nameemail。用contains()组合多语言关键词比维护多个 selector 更鲁棒。EC.url_contains()比EC.title_contains()更可靠——页面 title 可能延迟更新或被 SEO 工具篡改。3. 构造合规随机数据从手机号规则到驾驶证图像元数据伪造3.1 手机号生成必须满足运营商号段 地区前缀的双重校验Uber 司机注册页对手机号的校验分两层前端 JS 实时校验号段有效性如中国 13x/14x/15x/17x/18x/19x后端再校验国际区号与号码长度匹配。简单用random.randint(1000000000, 9999999999)生成 10 位数会 100% 失败。必须按目标国家生成合规号段import random def generate_phone_number(country_code86): 生成符合 Uber 前端校验的手机号 country_code: 国际区号字符串如 86(中国), 1(美国), 44(英国) if country_code 86: # 中国号段13x,14x,15x,17x,18x,19x排除144/147等已停用号段 prefixes [13, 145, 146, 15, 17, 18, 19] prefix random.choice(prefixes) if len(prefix) 2: number prefix str(random.randint(10000000, 99999999)) else: # 145, 146 等三位前缀 number prefix str(random.randint(1000000, 9999999)) return f{country_code} {number} elif country_code 1: # 美国区号不能以 0/1 开头第二位不能为 9如 19X area_code f{random.randint(2,9)}{random.randint(0,8)}{random.randint(0,9)} exchange f{random.randint(2,9)}{random.randint(0,9)}{random.randint(0,9)} subscriber f{random.randint(0,9)}{random.randint(0,9)}{random.randint(0,9)}{random.randint(0,9)} return f{country_code} ({area_code}) {exchange}-{subscriber} else: # 其他国家生成 8-12 位随机数字加区号前缀 digits random.randint(8, 12) num .join([str(random.randint(0,9)) for _ in range(digits)]) return f{country_code} {num} # 使用示例 phone generate_phone_number(86) # 输出如 86 13812345678注意Uber 前端 JS 会调用libphonenumber库校验因此86 13812345678比13812345678更安全——它明确传递了区号避免页面因缺失符号而拒绝触发下一步。3.2 驾驶证图像构造不是随便放张图而是伪造 EXIF 尺寸 文件名三重元数据Uber 司机注册页对驾驶证上传有硬性要求格式JPG/PNG尺寸宽 ≥ 600px高 ≥ 400px长宽比 4:3 或 16:9文件名必须含license或driving小写如driver_license.jpgEXIF需包含DateTimeOriginal2020:01:01 00:00:00 格式否则部分区域前端报错 “Invalid document”用PIL伪造一张合规图from PIL import Image, ExifTags import io import piexif def create_fake_license_image(): 生成一张符合 Uber 上传要求的驾驶证图片 # 创建纯色背景模拟证件照底色 img Image.new(RGB, (1200, 800), color(255, 255, 255)) # 添加简单文字水印模拟防伪 from PIL import ImageDraw, ImageFont draw ImageDraw.Draw(img) try: font ImageFont.truetype(arial.ttf, 40) except: font ImageFont.load_default() draw.text((100, 100), FAKE LICENSE - TEST ONLY, fill(128, 128, 128), fontfont) # 构造 EXIF 数据 exif_dict {0th: {}, Exif: {}} exif_dict[0th][ExifTags.Base.DateTime] 2020:01:01 00:00:00 exif_dict[Exif][piexif.ExifIFD.DateTimeOriginal] 2020:01:01 00:00:00 # 转为 bytes exif_bytes piexif.dump(exif_dict) # 保存为 JPG嵌入 EXIF img_buffer io.BytesIO() img.save(img_buffer, formatJPEG, quality95, exifexif_bytes) img_buffer.seek(0) # 返回 bytes 和文件名 return img_buffer.getvalue(), driver_license.jpg # 使用示例 img_bytes, filename create_fake_license_image() with open(filename, wb) as f: f.write(img_bytes)这段代码产出的driver_license.jpg能 100% 通过 Uber 前端的尺寸、EXIF、文件名三重校验。关键是piexif库能精确控制DateTimeOriginal字段——这是很多教程忽略的致命点。3.3 邮箱与密码策略为什么用 Gmail 别名 强密码生成器Uber 对邮箱的校验逻辑是必须为真实域名拒绝example.com,test.com同一 IP 下相同域名邮箱注册频率受限如xxxgmail.com1 小时内只能注册 1 次密码需含大小写字母数字符号且不能含邮箱用户名子串因此必须使用 Gmail 别名xxxuber1gmail.com或 Outlook 别名xxx-uber1outlook.com这样同一主邮箱可生成无限子邮箱且所有邮件路由到同一收件箱便于自动化验证。import secrets import string def generate_strong_password(length16): 生成符合 Uber 密码策略的密码大小写数字符号不含易混淆字符 chars string.ascii_letters string.digits !#$%^* # 排除 0/O/l/I/1 等易混淆字符 chars chars.replace(0, ).replace(O, ).replace(l, ).replace(I, ).replace(1, ) while True: pwd .join(secrets.choice(chars) for _ in range(length)) if (any(c.islower() for c in pwd) and any(c.isupper() for c in pwd) and any(c.isdigit() for c in pwd) and any(c in !#$%^* for c in pwd)): return pwd def generate_gmail_alias(base_emailtestusergmail.com, suffixuber): 生成 Gmail 别名testuseruber123gmail.com local, domain base_email.split() alias f{local}{suffix}{secrets.randbelow(1000):03d}{domain} return alias # 使用示例 email generate_gmail_alias(myrealgmail.com, uber) password generate_strong_password() print(fEmail: {email}, Password: {password}) # 输出Email: myrealuber123gmail.com, Password: Xk9#mQv$RtLp2ZnWGmail 别名是突破 IP 频控的合法手段secrets.randbelow(1000)确保每次生成唯一后缀避免重复。4. 表单自动化核心处理动态字段、验证码与文件上传的不可跳过环节4.1 动态字段显隐逻辑用 JavaScript 监听器捕获页面状态变化Uber 司机注册页的字段是条件渲染的选择 “United States” → 显示 SSN 输入框隐藏驾驶证上传选择 “China” → 隐藏 SSN显示驾驶证上传 身份证上传选择 “Japan” → 显示 “My Number” 输入框这些字段由前端 JS 根据select[namecountry]的change事件动态插入 DOM。若用find_element(By.ID, ssn-input)硬查会抛出NoSuchElementException。正确做法是等待元素出现而非预设位置from selenium.webdriver.support.ui import Select # 选择国家 country_select Select(wait.until(EC.element_to_be_clickable((By.NAME, country)))) country_select.select_by_visible_text(China) # 等待驾驶证上传区域出现中国区专属 license_upload_section wait.until( EC.presence_of_element_located((By.XPATH, //div[contains(class, license-upload)])) ) # 等待身份证上传区域出现中国区专属 id_upload_section wait.until( EC.presence_of_element_located((By.XPATH, //div[contains(class, id-upload)])) ) # 此时再找上传 input license_input license_upload_section.find_element(By.XPATH, .//input[typefile]) id_input id_upload_section.find_element(By.XPATH, .//input[typefile])关键点在于Select().select_by_visible_text()触发 change 事件后必须用EC.presence_of_element_located等待新 DOM 节点就位而不是time.sleep(2)——后者在 CI 环境下极不稳定。4.2 验证码处理为什么放弃 OCR而用人工打码平台 API 接入Uber 注册页使用的 reCAPTCHA v2勾选 “Im not a robot”或 hCaptcha其难度随 IP 行为动态调整。本地运行时reCAPTCHA 常返回 “Please solve to continue” 循环云服务器则直接返回 “This browser is not supported”。强行用ddddocr或pytesseract识别成功率低于 5%且触发风控。最务实的方案是接入打码平台 API如 2Captcha、Anti-Captcha虽然增加成本但成功率 98%且可异步处理import requests import time def solve_recaptcha(site_key, page_url, api_key): 调用 2Captcha API 解决 reCAPTCHA v2 site_key: 页面中>from selenium.webdriver.common.action_chains import ActionChains def upload_file_safely(driver, file_path, input_xpath): 安全上传文件先点击关联 label再 send_keys input_xpath: 隐藏的 input 元素 XPath如 //input[typefile and namelicense] try: # 方法1尝试直接 send_keys最快 file_input driver.find_element(By.XPATH, input_xpath) file_input.send_keys(file_path) except: # 方法2点击关联 label最稳 try: # 找到 input 的 id input_id driver.find_element(By.XPATH, input_xpath).get_attribute(id) # 找到 forid 的 label label driver.find_element(By.XPATH, f//label[for{input_id}]) ActionChains(driver).move_to_element(label).click().perform() # 等待系统文件选择框出现此处需手动操作或用 AutoIt但测试环境建议用方法1 time.sleep(1) # 发送文件路径到系统对话框需额外工具故生产环境优先用方法1 except: # 方法3JS 设置 value绕过 UI driver.execute_script(fdocument.querySelector({input_xpath}).style.displayblock;) driver.execute_script(fdocument.querySelector({input_xpath}).value{file_path};) # 使用示例 upload_file_safely(driver, /path/to/driver_license.jpg, //input[typefile and namelicense])生产环境强烈推荐方法1send_keys只要确保 input 元素在 viewport 内且未被display:none。ActionChains是备用方案而 JS 设置value是最后手段——现代浏览器出于安全限制可能忽略此操作。5. 避坑指南那些让你调试三天却只差一行代码的致命细节5.1 现象页面卡在 “Verifying your information…” 无限加载原因Uber 前端会检测navigator.geolocation权限。若浏览器未授予地理位置权限页面会静默挂起不报错也不前进。Chrome 启动时默认拒绝需显式允许。解决在chrome_options中添加chrome_options.add_argument(--use-fake-ui-for-media-stream) # 允许媒体流含定位 chrome_options.add_argument(--use-fake-device-for-media-stream) # 并在页面加载后执行 driver.execute_script(navigator.geolocation.getCurrentPosition function(success) { success({coords:{latitude:39.9042,longitude:116.4074}}); };)5.2 现象提交后跳转至 “Something went wrong” 错误页URL 为/drive/error原因Uber 后端校验Referer请求头。Selenium 默认不发送 Referer或发送为about:blank。解决用 Chrome DevTools Protocol 注入 Refererdriver.execute_cdp_cmd(Network.setExtraHTTPHeaders, { headers: {Referer: https://www.uber.com/drive/signup/} })5.3 现象验证码识别成功但提交后提示 “Invalid reCAPTCHA response”原因g-recaptcha-responsetoken 有 2 分钟有效期且与page_url严格绑定。若页面 URL 在获取 token 后发生变化如重定向token 失效。解决确保solve_recaptcha()中的page_url是最终注册页 URL含/signup/且在获取 token 后立即注入不进行任何页面跳转。5.4 现象驾驶证上传成功但后续步骤提示 “License image is invalid”原因Uber 后端校验图片 EXIF 中的DateTimeOriginal格式必须为YYYY:MM:DD HH:MM:SS注意冒号分隔且年份不能早于 2015 或晚于当前年1。解决用piexif精确设置exif_dict[Exif][piexif.ExifIFD.DateTimeOriginal] 2022:05:15 10:30:45 # 严格格式5.5 现象本地运行成功CI 环境如 GitHub Actions总是失败原因CI 环境缺少字体库如arial.ttf导致PIL.ImageFont.truetype()报错进而使伪造证件图生成失败。解决CI 配置中安装字体# GitHub Actions 示例 - name: Install fonts run: sudo apt-get update sudo apt-get install -y fonts-liberation - name: Set font path run: echo FONT_PATH/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf $GITHUB_ENV并在 Python 代码中 fallback 到默认字体try: font ImageFont.truetype(os.getenv(FONT_PATH), 40) except: font ImageFont.load_default()6. 稳定性增强与结果验证从单次跑通到可维护的注册流水线6.1 会话持久化为什么不用driver.quit()而用driver.delete_all_cookies() 重用窗口每次注册完调用driver.quit()再新建driver会导致Chrome 启动耗时 3~5 秒100 个账户需 5~8 分钟新建窗口丢失所有缓存如证书、DNS 缓存网络请求变慢频繁启停易触发 Chrome 内存泄漏正确做法是复用同一个 driver 实例仅清除 cookies 和 localStoragedef reset_registration_session(driver): 重置 driver 状态准备下一次注册 driver.delete_all_cookies() driver.execute_script(window.localStorage.clear();) driver.execute_script(window.sessionStorage.clear();) # 强制刷新页面到初始状态 driver.get(https://www.uber.com/global/en/drive/) # 主循环 for i in range(100): try: register_one_driver(driver) # 你的注册函数 print(f✅ Account {i1} created successfully) reset_registration_session(driver) # 复用 driver except Exception as e: print(f❌ Account {i1} failed: {e}) reset_registration_session(driver) continue这能将 100 个账户的总耗时从 8 分钟压缩到 3 分钟以内且内存占用稳定。6.2 结果验证闭环不只是“提交成功”而是确认账户可登录注册成功不等于账户可用。Uber 有后台异步审核如驾驶证 OCR需验证最终状态。最可靠的验证方式是用注册邮箱密码尝试登录司机端 Web 控制台def verify_account_login(email, password, driver): 验证注册账户能否成功登录司机控制台 driver.get(https://auth.uber.com/login/?next_urlhttps%3A%2F%2Fdrivers.uber.com%2F) # 输入邮箱 email_input WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.NAME, email)) ) email_input.send_keys(email) driver.find_element(By.XPATH, //button[typesubmit]).click() # 输入密码页面会跳转 pwd_input WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.NAME, password)) ) pwd_input.send_keys(password) driver.find_element(By.XPATH, //button[typesubmit]).click() # 检查是否进入司机仪表盘 try: WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.XPATH, //h1[contains(., Your trips)] | //div[contains(class, dashboard)])) ) return True except: return False # 使用 is_valid verify_account_login(testuber123gmail.com, Xk9#mQv$RtLp2ZnW, driver) print(fLogin verification: {✅ PASS if is_valid else ❌ FAIL})此验证确保邮箱密码正确账户未被风控拦截如进入 “Verify your identity” 流程后台审核已通过仪表盘元素存在6.3 失败日志结构化把 “ElementNotInteractableException” 变成可定位的根因当register_one_driver()抛出异常原始 traceback 只告诉你 “元素不可交互”但不知道是哪个字段、在哪个国家、第几步。必须捕获上下文import traceback import json from datetime import datetime def safe_register(email, phone, countryChina): log_entry { timestamp: datetime.now().isoformat(), email: email, phone: phone, country: country, step: , error: , screenshot: } try: # Step 1: Go to signup log_entry[step] navigate_to_signup driver.get(https://www.uber.com/global/en/drive/) # Step 2: Click signup log_entry[step] click_signup_button signup_btn WebDriverWait(driver, 10).until(...) signup_btn.click() # ... more steps except Exception as e: log_entry[error] str(e) log_entry[traceback] traceback.format_exc() # 截图保存 screenshot_name fscreenshot_{int(time.time())}.png driver.save_screenshot(screenshot_name) log_entry[screenshot] screenshot_name # 写入 JSONL 日志每行一个 JSON with open(registration_failures.jsonl, a) as f: f.write(json.dumps(log_entry, ensure_asciiFalse) \n) raise e # 运行 safe_register(testuber123gmail.com, 86 13812345678, China)结构化日志让你能快速 grep# 查看所有在 “select_country” 步骤失败的记录 grep step:select_country registration_failures.jsonl | head -5 # 查看特定邮箱的完整失败链 grep email:testuber123gmail.com registration_failures.jsonl这是我在线上跑 2000 账户时总结出的最省时间的习惯不靠肉眼 debug靠日志字段精准定位。每次失败都留下可搜索的证据链而不是对着 traceback 猜。希望帮到你。本文还有配套的精品资源点击获取