You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:05:01