使用Python爬取Uber价格估算页面遇元素定位失败、406报错求助
核心问题原因
- Selenium定位报错的直接原因:
find_element_by_class_name()方法仅支持传入单个CSS类名,你传入的bn rw bp nk ih cr vk是元素绑定的7个独立类名,空格是类名分隔符,语法层面就不支持该传参方式。另外这类字符串是Tailwind框架生成的动态原子类,Uber每次前端发版都会变更类名组合,完全不适合作为长期爬取的定位依据,就算本次定位成功,后续页面更新会直接失效。 - requests返回406的原因:Uber估价页有多层反爬校验,除了User-Agent外还会校验TLS指纹、请求上下文、Cookie链路,纯requests无浏览器环境无法通过校验;且价格数据完全由JavaScript异步拉取,初始静态HTML中不包含任何价格内容,BeautifulSoup+requests的方案无法实现需求。
实现方案
推荐方案(长期稳定、资源占用低):直接调用Uber前端使用的估价接口
模拟浏览器渲染页面解析HTML是效率最低的方案,长期定时跑稳定性差。最可靠的方式是抓页面加载价格时调用的内部接口:
- 打开浏览器开发者工具,切换到Network面板,筛选XHR请求,手动输入一次上下车点,找到名称带
estimate的请求,该请求返回结构化JSON数据,直接包含所有车型的预估价格、等待时长、行程距离等全部需要的信息,无需解析HTML。 - 请求接口时携带正常访问页面生成的Cookie、与浏览器完全一致的请求头;上下车点参数传经纬度(提前把目标地址转成对应经纬度即可),比传地址字符串匹配准确率更高,不会出现地址识别错误。
- 定时任务每次运行时,先请求一次估价页首页获取最新有效Cookie,再调用估价接口,整体请求耗时不到1秒,资源占用远低于Selenium,也不会受前端页面结构调整影响。
临时方案(快速修正现有Selenium代码)
如果暂时不想抓接口,按以下规则修正现有代码即可正常拿到数据:
- 替换定位规则,弃用动态类名:优先找元素上的
data-testid属性(前端为自动化测试预留的属性,稳定性远高于CSS类名)、固定车型文本作为定位锚点,使用XPath做相对定位。 - 替换固定的
time.sleep()为显式等待WebDriverWait,设置10-15秒超时,等对应元素真正渲染完成后再做操作,避免网络波动导致元素还没加载就触发定位报错。 - 输入地址后不要直接按回车,等地址下拉建议框弹出后点击第一个匹配项,避免地址匹配错误导致页面根本没加载出价格区域。
修正后的可运行参考代码:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.firefox.options import Options from selenium import webdriver from selenium.webdriver.common.keys import Keys import time firefox_webdriver_path = '/usr/local/bin/geckodriver' # 加配置规避自动化检测 opts = Options() opts.add_argument("--headless=new") opts.add_argument("--disable-blink-features=AutomationControlled") driver = webdriver.Firefox(executable_path=firefox_webdriver_path, options=opts) driver.get('https://www.uber.com/global/en/price-estimate/') wait = WebDriverWait(driver, 15) # 输入上车点 pickup_input = wait.until(EC.presence_of_element_located((By.NAME, 'pickup'))) pickup_input.clear() pickup_input.send_keys('你的上车点地址') time.sleep(1) # 选中下拉列表第一个匹配地址,避免按回车匹配错误 wait.until(EC.presence_of_element_located((By.XPATH, '//ul[@role="listbox"]/li[1]'))).click() # 输入目的地 dest_input = wait.until(EC.presence_of_element_located((By.NAME, 'destination'))) dest_input.clear() dest_input.send_keys('你的目的地地址') time.sleep(1) wait.until(EC.presence_of_element_located((By.XPATH, '//ul[@role="listbox"]/li[1]'))).click() # 等价格加载完成,用稳定的testid或者价格特征定位,不要用动态类名 price_rows = wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@data-testid="price-result-row"]//span[contains(text(), "$") or contains(text(), "¥")]'))) for price in price_rows: print(price.text) driver.quit()
长期运行注意事项
- 控制请求频率,定时任务间隔不要低于15分钟,使用住宅IP发起请求,避免被反爬系统封禁。
- 所有定位逻辑不要依赖动态生成的CSS类名,优先选择id、data-*属性、固定可见文本作为定位锚点,降低页面迭代导致的代码失效概率。
内容的提问来源于stack exchange,提问作者ttendril
相关产品推荐
相关产品推荐

