Python WebSocket爬取轮胎库存遇握手取消错误求助
解决StockHub WebSocket握手失败及轮胎库存/到货时间监控方案
一、问题根源分析
你遇到的"error":"Handshake was canceled"错误,核心原因是未正确完成SignalR的协商流程:
- 必须先通过POST请求获取
connectionId和accessToken等关键参数 - WebSocket连接URL需要携带协商返回的参数,同时请求头要匹配浏览器的请求特征
二、完整实现代码
1. 完成协商请求,获取连接参数
import requests import websocket import json from typing import Dict # 模拟浏览器请求头,需保持与真实请求一致 HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Accept": "*/*", "Accept-Language": "zh-CN,zh;q=0.9", "Content-Type": "application/json; charset=utf-8", "Origin": "https://www.jaxtyres.com.au", "Referer": "https://www.jaxtyres.com.au/tyres/hankook/ventus-s1-evo2-k117?sku=ha2254517k117w" } def negotiate_signalr() -> Dict: """发送协商请求,获取SignalR连接所需参数""" negotiate_url = "https://www.jaxtyres.com.au/StockHub/negotiate?negotiateVersion=1" payload = json.dumps({}) response = requests.post(negotiate_url, headers=HEADERS, data=payload) response.raise_for_status() # 保存Cookie,后续WebSocket连接需要使用 global cookies cookies = requests.utils.dict_from_cookiejar(response.cookies) return response.json()
2. 构造正确WebSocket连接并处理消息
def on_message(ws, message): """解析WebSocket消息,提取到货时间与库存状态""" try: data = json.loads(message) # SignalR消息的核心数据通常在arguments字段中 if "arguments" in data: for item in data["arguments"]: if "stockDetails" in item or "arrivalDate" in item: print("=== 库存/到货信息 ===") print(json.dumps(item, indent=2, ensure_ascii=False)) except json.JSONDecodeError: print(f"消息解析失败: {message}") def on_error(ws, error): print(f"连接错误: {error}") def on_close(ws, close_status_code, close_msg): print(f"连接关闭 | 状态码: {close_status_code} | 消息: {close_msg}") def on_open(ws): """连接建立后发送订阅请求,可添加多个SKU监控""" subscribe_payload = json.dumps({ "H": "StockHub", "M": "SubscribeToStockUpdates", "A": [ {"sku": "ha2254517k117w"}, # 可继续添加其他SKU,最多10个 # {"sku": "your-second-sku"}, # {"sku": "your-third-sku"} ], "I": 1 }) ws.send(subscribe_payload) print("已发送库存订阅请求") def start_websocket(negotiate_data: Dict): """启动WebSocket连接""" # 构造带协商参数的WebSocket URL ws_url = ( f"wss://www.jaxtyres.com.au/StockHub?id={negotiate_data['connectionId']}" f"&access_token={negotiate_data['accessToken']}" ) # 拼接Cookie到请求头 ws_headers = HEADERS.copy() ws_headers["Cookie"] = "; ".join([f"{k}={v}" for k, v in cookies.items()]) websocket.enableTrace(True) ws = websocket.WebSocketApp( ws_url, header=ws_headers, on_message=on_message, on_error=on_error, on_close=on_close ) ws.on_open = on_open ws.run_forever() if __name__ == "__main__": try: # 第一步:执行协商流程 negotiate_result = negotiate_signalr() print("协商成功,连接参数:", negotiate_result) # 第二步:启动WebSocket监控 start_websocket(negotiate_result) except Exception as e: print(f"执行出错: {e}")
三、多SKU监控扩展
只需修改on_open函数中的A参数数组,添加需要监控的SKU即可,最多可同时监控10个。
四、关键注意事项
- 请求头完整性:务必保持
User-Agent、Origin、Referer与浏览器请求一致,否则会被网站拦截 - Cookie传递:协商请求返回的Cookie必须带入WebSocket连接,否则握手会失败
- SignalR消息格式:必须严格遵循
{"H": Hub名称, "M": 方法名, "A": 参数数组, "I": 消息ID}的格式发送订阅请求 - 重连机制:若连接意外断开,可添加自动重连逻辑,重新执行协商+连接流程
内容的提问来源于stack exchange,提问作者user20268089
相关产品推荐
相关产品推荐

