4. REST API实战:使用requests库发送GET/POST请求、处理签名认证、解析JSON响应

做市机器人跟交易所打交道,最基础的就是REST API。说白了,就是通过HTTP协议给交易所服务器发指令——查余额、下订单、撤单,全得靠它。我个人习惯把REST API比作「交易所的门卫」,你递上正确的证件(签名),它才让你进门办事。

4.1 准备工作:安装requests库

Python自带的urllib也能发HTTP请求,但用起来太啰嗦。我建议直接用requests库,代码简洁得多。安装就一行命令:

pip install requests

嗯,这里要注意:如果你在虚拟环境里做开发,记得先激活环境再装。我在项目中遇到过有人装到了全局环境,结果项目跑起来报错找不到模块,排查了半天才发现是环境搞混了。

4.2 发送GET请求:查行情、查账户

GET请求通常用来获取数据,比如查最新价格、查账户余额。拿币安的现货行情接口举个例子:

import requests

# 查BTC/USDT的最新价格
url = "https://api.binance.com/api/v3/ticker/price"
params = {"symbol": "BTCUSDT"}

response = requests.get(url, params=params)
print(response.json())
# 输出:{'symbol': 'BTCUSDT', 'price': '50000.00'}

为什么会这样?因为requests库会自动把params字典拼接到URL后面,变成?symbol=BTCUSDT。你想想看,如果手动拼接字符串,万一参数里有特殊字符还得做URL编码,多麻烦。

查账户余额就需要API Key了。交易所会给你一对密钥:

  • API Key:相当于你的用户名,明文传输
  • Secret Key:相当于你的密码,用来签名,绝不能泄露
api_key = "你的API_KEY"
secret_key = "你的SECRET_KEY"

headers = {"X-MBX-APIKEY": api_key}
url = "https://api.binance.com/api/v3/account"

response = requests.get(url, headers=headers)
print(response.json())

注意:这里只是演示,实际生产环境绝对不能把密钥硬编码在代码里。我曾经见过有人把密钥提交到GitHub,几分钟内账户就被盗了。建议用环境变量或配置文件来管理。

4.3 发送POST请求:下单、撤单

POST请求用来提交数据,比如下订单。下单接口通常需要签名认证,因为涉及资金操作。看一个币安的下单示例:

import requests
import hashlib
import hmac
import time

api_key = "你的API_KEY"
secret_key = "你的SECRET_KEY"

# 构造请求参数
params = {
    "symbol": "BTCUSDT",
    "side": "BUY",
    "type": "LIMIT",
    "timeInForce": "GTC",
    "quantity": "0.001",
    "price": "50000",
    "timestamp": int(time.time() * 1000)
}

# 生成签名
query_string = "&".join([f"{k}={v}" for k, v in params.items()])
signature = hmac.new(
    secret_key.encode(),
    query_string.encode(),
    hashlib.sha256
).hexdigest()
params["signature"] = signature

headers = {"X-MBX-APIKEY": api_key}
url = "https://api.binance.com/api/v3/order"

response = requests.post(url, headers=headers, data=params)
print(response.json())

这里有个关键点:timestamp参数必须是毫秒级时间戳。交易所会用这个时间戳来防止重放攻击——如果请求时间跟服务器时间差太多,会被直接拒绝。我建议每次请求前都同步一下服务器时间,避免因本地时钟偏差导致签名失败。

避坑指南:我曾经遇到过一个问题——签名总是验证失败。排查了半天,发现是params字典的键值顺序变了。Python 3.7之前字典是无序的,同样的参数每次生成的query_string可能不一样,签名自然对不上。解决方案是用OrderedDict或者手动排序。

4.4 签名认证的核心逻辑

签名认证说白了就是「证明你是你」。交易所给你Secret Key,你用它对请求参数做HMAC-SHA256哈希,生成一个签名。服务器收到请求后,用同样的Secret Key重新算一遍签名,如果一致就放行。

整个流程可以用这张图来理解:

客户端 API Key + 参数 + 签名 交易所服务器 验证签名是否匹配 签名生成过程 参数拼接 → query_string = "symbol=BTCUSDT&side=BUY&timestamp=..." HMAC-SHA256(secret_key, query_string) → signature 最终请求:参数 + signature

4.5 解析JSON响应

交易所返回的数据基本都是JSON格式。requests库的.json()方法可以直接把响应体解析成Python字典或列表,用起来非常方便。

response = requests.get(url, params=params)
data = response.json()

# 检查是否成功
if "code" in data and data["code"] != 0:
    print(f"请求失败:{data['msg']}")
else:
    print(f"当前价格:{data['price']}")

但有个坑要注意:不是所有响应都是200状态码。比如参数错误会返回400,签名错误会返回401,频率超限会返回429。我建议每次请求都检查状态码:

if response.status_code != 200:
    print(f"HTTP错误:{response.status_code}")
    print(f"错误详情:{response.text}")
    # 根据错误码做相应处理
    if response.status_code == 429:
        time.sleep(60)  # 频率限制,等一分钟再试
    elif response.status_code == 401:
        # 签名过期或密钥错误,需要重新生成签名
        pass
小技巧:调试阶段可以把response.text打印出来看看原始响应内容。有时候JSON解析失败,是因为交易所返回了HTML格式的错误页面。我遇到过好几次,都是因为请求头没加User-Agent被WAF拦截了。

4.6 实战中的常见问题

问题 原因 解决方案
签名验证失败 参数顺序不一致、时间戳偏差 用OrderedDict排序参数,同步服务器时间
请求被限频 短时间内请求太多 加sleep延迟,或者用WebSocket替代
JSON解析报错 响应不是JSON格式 先打印response.text排查
连接超时 网络问题或交易所挂了 设置timeout参数,加重试机制

嗯,最后说一句:REST API虽然简单,但做市机器人对延迟要求很高。我个人建议把REST API只用来做非高频操作(比如查余额、设置参数),真正的下单撤单还是用WebSocket更靠谱。不过那是后面章节的内容了,先把REST API玩熟再说。

核心要点回顾:
  • GET请求查数据,POST请求写数据
  • 签名认证用HMAC-SHA256,注意参数顺序和时间戳
  • 解析JSON用.json(),但别忘了检查状态码
  • 密钥绝不能硬编码,用环境变量管理
专注资料整理