如何使用代理抓取 Binance REST API:核心思路
在量化交易和加密数据工程中,binance api scraping 是构建价格与订单簿数据集的基础工作。Binance 的公开 REST API 提供了 K 线、深度、24 小时行情等端点,但采用了基于权重的 IP 级限速机制:每个 IP 每分钟约有 6000 权重预算,超限会触发 HTTP 429,持续违规会导致 HTTP 418 IP 封禁。通过 住宅代理 轮换 IP,可以将权重消耗分散到多个出口地址,从而在合规范围内实现大规模数据采集。本文将带你从端点映射、限速机制到生产级代码实现,完整掌握 scrape binance market data 的最佳实践。
合规声明:本文仅涉及 Binance 公开市场数据(public market data)的采集,不涉及账户交易端点或私有数据。请遵守 Binance 服务条款,尊重 API 权重限制,在条款要求官方 API 访问的场景下优先使用官方接口。详情参阅 Binance 服务条款。
Binance 权重限速机制:为什么单 IP 采集会快速耗尽预算
Binance 对 REST API 请求采用基于权重的速率限制(weight-based rate limiting),而非简单的请求次数限制。不同端点消耗不同权重,响应头中的 X-MBX-USED-WEIGHT-1M 标头会告诉你当前 IP 在过去 1 分钟内已消耗的权重总量。
根据 Binance 官方 API 文档,关键规则如下:
- 每 IP 每分钟权重上限:约 6000(具体值可能调整,以官方文档为准)
- HTTP 429:表示已超限,客户端应立即降低请求频率
- HTTP 418:表示 IP 已被自动封禁,封禁时间通过
Retry-After响应头返回,通常为 2 分钟到数天不等 - HTTP 451:因法律原因不可用,通常出现在 Binance.com 对美国地区的地理限制场景
问题在于,某些端点的权重消耗非常高。例如 /api/v3/depth(订单簿)端点在请求 limit=5000 时消耗 50 权重,limit=1000 时消耗 20 权重。如果你每秒轮询一次 limit=5000 的深度数据,仅 120 秒就会消耗 6000 权重——即 2 分钟内耗尽整个 IP 的每分钟预算。这就是为什么 binance rest api rate limit proxy 策略至关重要。
关键公开市场数据端点映射
以下是 Binance 公开 REST API 中最常用的四个市场数据端点,以及它们的权重消耗和典型用途:
| 端点 | 用途 | 典型权重 | 说明 |
|---|---|---|---|
GET /api/v3/klines |
K 线/蜡烛图数据 | 1-2(基于 limit) | limit ≤ 100 时权重为 1,limit > 500 时权重为 5,limit=1000 时权重为 10 |
GET /api/v3/depth |
订单簿快照 | 1-50 | limit=100 权重 1,limit=1000 权重 20,limit=5000 权重 50 |
GET /api/v3/ticker/24hr |
24 小时行情统计 | 1-40 | 单交易对权重 1,全市场(无 symbol 参数)权重 40 |
GET /api/v3/ticker/price |
最新价格 | 1-2 | 单交易对权重 1,全市场权重 2 |
每次请求后,务必检查响应头 X-MBX-USED-WEIGHT-1M 的值。这是你动态调整请求频率的核心依据——当该值接近 5000 时,就应当降低轮询频率或切换出口 IP。
代理轮换策略:分散权重消耗
核心思路很简单:如果单个 IP 每分钟有 6000 权重预算,那么 N 个 IP 就有 N × 6000 的总预算。通过 住宅代理 轮换,每个请求可以从不同的住宅 IP 发出,将权重分散到多个地址上。
对于 Binance.com 与 Binance.US 的地理分割问题,Binance.com 对美国地区返回 HTTP 451。使用 ProxyHat 的 -country-US 地理定位可以确保请求从美国 IP 发出,从而访问 Binance.US;反之,使用非美国 IP 则可以正常访问 Binance.com。
ProxyHat 网关连接参数:
- HTTP 网关:
gate.proxyhat.com:8080 - SOCKS5 网关:
gate.proxyhat.com:1080 - 用户名格式:
user-country-DE-city-berlin(地理定位)、user-session-abc123(粘性会话)
更多配置细节请参考 ProxyHat 官方文档。
代码实现:Python requests 逐请求轮换 IP
以下示例展示如何使用 Python requests 库通过 ProxyHat 逐请求轮换住宅 IP 抓取 Binance K 线数据,同时实现权重感知限速和指数退避重试。
import requests
import time
import random
import logging
from urllib.parse import quote
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
logger = logging.getLogger(__name__)
# ProxyHat credentials
PROXYHAT_USER = "your_username"
PROXYHAT_PASS = "your_password"
PROXYHAT_GATE = "gate.proxyhat.com"
PROXYHAT_PORT = 8080
BINANCE_BASE = "https://api.binance.com"
# Weight tracking
used_weight = 0
WEIGHT_SOFT_LIMIT = 5000 # soft threshold before rotating
def get_proxy_url(country=None, session_id=None):
"""Build ProxyHat proxy URL with optional geo-targeting and sticky session."""
username = PROXYHAT_USER
if country:
username += f"-country-{country}"
if session_id:
username += f"-session-{session_id}"
return f"http://{username}:{PROXYHAT_PASS}@{PROXYHAT_GATE}:{PROXYHAT_PORT}"
def fetch_klines(symbol, interval, limit=500, max_retries=5):
"""Fetch klines with IP rotation, weight-aware throttling, and exponential backoff."""
global used_weight
url = f"{BINANCE_BASE}/api/v3/klines"
params = {"symbol": symbol, "interval": interval, "limit": limit}
for attempt in range(max_retries):
# Rotate IP per request — each request gets a fresh residential IP
proxy_url = get_proxy_url()
proxies = {"http": proxy_url, "https": proxy_url}
try:
resp = requests.get(url, params=params, proxies=proxies, timeout=15)
# Track weight from response header
weight_header = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight_header:
used_weight = int(weight_header)
logger.info(f"Used weight (1m): {used_weight} | Status: {resp.status_code}")
if resp.status_code == 200:
return resp.json()
elif resp.status_code == 429:
# Rate limited — back off significantly
retry_after = int(resp.headers.get("Retry-After", 60))
logger.warning(f"429 received. Sleeping {retry_after}s...")
time.sleep(retry_after)
continue
elif resp.status_code == 418:
# IP banned — this shouldn't happen with rotation, but handle it
retry_after = int(resp.headers.get("Retry-After", 120))
logger.error(f"418 IP banned! Sleeping {retry_after}s...")
time.sleep(retry_after)
continue
elif resp.status_code == 451:
logger.error("451 geo-blocked. Try country-US for Binance.US.")
# Switch to US geo-targeting
proxy_url = get_proxy_url(country="US")
proxies = {"http": proxy_url, "https": proxy_url}
resp = requests.get(url, params=params, proxies=proxies, timeout=15)
if resp.status_code == 200:
return resp.json()
continue
else:
logger.warning(f"Unexpected status {resp.status_code}: {resp.text[:200]}")
except requests.RequestException as e:
logger.warning(f"Request error attempt {attempt+1}: {e}")
# Exponential backoff with jitter
backoff = (2 ** attempt) + random.uniform(0, 1)
logger.info(f"Backing off {backoff:.1f}s before retry...")
time.sleep(backoff)
raise RuntimeError(f"Failed after {max_retries} attempts for {symbol} {interval}")
# Example: fetch BTCUSDT 1m klines
if __name__ == "__main__":
data = fetch_klines("BTCUSDT", "1m", limit=500)
print(f"Got {len(data)} candles. First: open={data[0][1]}, high={data[0][2]}")
这个示例的关键设计点:每次请求使用新的 ProxyHat 住宅 IP,通过 X-MBX-USED-WEIGHT-1M 响应头追踪权重消耗,对 429/418 状态码实施退避策略,对 451 地理限制自动切换到美国出口。
代码实现:ProxyHat SDK 逐请求轮换(对比写法)
以下是使用 ProxyHat SDK 封装的等价实现,简化了代理 URL 构建逻辑,适合需要更高级代理管理的场景。
import httpx
import asyncio
import logging
import random
from typing import Optional
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
logger = logging.getLogger(__name__)
# ProxyHat SDK wrapper (simplified — adapt to actual SDK if available)
class ProxyHatClient:
"""ProxyHat proxy manager with per-request rotation and sticky sessions."""
def __init__(self, username: str, password: str,
gateway: str = "gate.proxyhat.com",
http_port: int = 8080,
socks5_port: int = 1080):
self.username = username
self.password = password
self.gateway = gateway
self.http_port = http_port
self.socks5_port = socks5_port
def http_proxy(self, country: Optional[str] = None,
session_id: Optional[str] = None) -> str:
"""Build HTTP proxy URL with geo-targeting and/or sticky session."""
user = self.username
if country:
user += f"-country-{country}"
if session_id:
user += f"-session-{session_id}"
return f"http://{user}:{self.password}@{self.gateway}:{self.http_port}"
def socks5_proxy(self, country: Optional[str] = None,
session_id: Optional[str] = None) -> str:
"""Build SOCKS5 proxy URL."""
user = self.username
if country:
user += f"-country-{country}"
if session_id:
user += f"-session-{session_id}"
return f"socks5://{user}:{self.password}@{self.gateway}:{self.socks5_port}"
# Initialize client
proxyhat = ProxyHatClient(username="your_username", password="your_password")
BINANCE_BASE = "https://api.binance.com"
async def fetch_depth_async(symbol: str, limit: int = 100,
max_retries: int = 5) -> dict:
"""Fetch order book depth with async httpx, IP rotation, and backoff."""
url = f"{BINANCE_BASE}/api/v3/depth"
params = {"symbol": symbol, "limit": limit}
async with httpx.AsyncClient(timeout=15.0) as client:
for attempt in range(max_retries):
# Fresh residential IP per request
proxy = proxyhat.http_proxy()
try:
resp = await client.get(url, params=params, proxy=proxy)
weight = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight:
logger.info(f"Weight: {weight} | Status: {resp.status_code}")
if resp.status_code == 200:
return resp.json()
elif resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 60))
logger.warning(f"429 — sleeping {retry_after}s")
await asyncio.sleep(retry_after)
elif resp.status_code == 451:
# Geo-blocked — retry with US exit
proxy = proxyhat.http_proxy(country="US")
resp = await client.get(url, params=params, proxy=proxy)
if resp.status_code == 200:
return resp.json()
else:
logger.warning(f"Status {resp.status_code}: {resp.text[:200]}")
except (httpx.ProxyError, httpx.ConnectTimeout, httpx.ReadTimeout) as e:
logger.warning(f"Proxy/network error attempt {attempt+1}: {e}")
# Exponential backoff with jitter
backoff = (2 ** attempt) + random.uniform(0, 1.5)
await asyncio.sleep(backoff)
raise RuntimeError(f"Depth fetch failed for {symbol}")
# Concurrency-limited batch fetch
async def fetch_multiple_symbols(symbols: list, limit: int = 100,
max_concurrent: int = 10) -> list:
"""Fetch depth for multiple symbols with semaphore-controlled concurrency."""
semaphore = asyncio.Semaphore(max_concurrent)
async def bounded_fetch(symbol):
async with semaphore:
return await fetch_depth_async(symbol, limit=limit)
results = await asyncio.gather(*[bounded_fetch(s) for s in symbols],
return_exceptions=True)
return list(zip(symbols, results))
if __name__ == "__main__":
symbols = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "ADAUSDT", "XRPUSDT"]
results = asyncio.run(fetch_multiple_symbols(symbols, limit=100, max_concurrent=5))
for sym, data in results:
if isinstance(data, dict):
print(f"{sym}: {len(data.get('bids', []))} bids, {len(data.get('asks', []))} asks")
else:
print(f"{sym}: ERROR — {data}")
这个异步版本使用 httpx.AsyncClient 配合 asyncio.Semaphore 控制并发,适合同时抓取多个交易对的深度数据。每个请求通过 ProxyHat SDK 获取新的住宅 IP,避免单 IP 权重超限。
粘性会话:K 线历史数据回填
当需要分页回填历史 K 线数据时,你可能希望在一段时间内保持同一个出口 IP,以利用该 IP 的权重预算。ProxyHat 的 -session-abc123 参数实现粘性会话——同一 session ID 的请求会路由到同一个住宅 IP。
import requests
import time
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
PROXYHAT_USER = "your_username"
PROXYHAT_PASS = "your_password"
BINANCE_BASE = "https://api.binance.com"
def build_sticky_proxy(session_id: str, country: str = None):
"""Build ProxyHat proxy URL with sticky session for paginated backfills."""
username = f"{PROXYHAT_USER}-session-{session_id}"
if country:
username += f"-country-{country}"
return f"http://{username}:{PROXYHAT_PASS}@gate.proxyhat.com:8080"
def backfill_klines(symbol: str, interval: str,
start_time: int, end_time: int,
limit_per_req: int = 1000) -> list:
"""Backfill historical klines using sticky session for weight budget reuse.
Uses startTime/endTime pagination. Each request consumes ~10 weight
(limit=1000). With 6000/min budget, we can do ~600 requests/min per IP.
"""
session_id = f"backfill-{symbol}-{int(time.time())}"
proxy_url = build_sticky_proxy(session_id)
proxies = {"http": proxy_url, "https": proxy_url}
all_candles = []
current_start = start_time
request_count = 0
while current_start < end_time:
url = f"{BINANCE_BASE}/api/v3/klines"
params = {
"symbol": symbol,
"interval": interval,
"startTime": current_start,
"endTime": end_time,
"limit": limit_per_req
}
try:
resp = requests.get(url, params=params, proxies=proxies, timeout=20)
request_count += 1
# Monitor weight
weight = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight and int(weight) > 5000:
logger.info(f"Weight at {weight} — cooling down 10s")
time.sleep(10)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 30))
logger.warning(f"429 — backing off {retry_after}s")
time.sleep(retry_after)
# Rotate to new session IP
new_session = f"backfill-{symbol}-{int(time.time())}"
proxy_url = build_sticky_proxy(new_session)
proxies = {"http": proxy_url, "https": proxy_url}
continue
if resp.status_code != 200:
logger.error(f"Status {resp.status_code}: {resp.text[:200]}")
time.sleep(5)
continue
candles = resp.json()
if not candles:
logger.info("No more data — backfill complete")
break
all_candles.extend(candles)
# Move startTime past the last candle's close time
current_start = candles[-1][6] + 1 # index 6 = close time
logger.info(f"Fetched {len(candles)} candles | total: {len(all_candles)} | req #{request_count}")
# Respect rate limit — small delay between requests
time.sleep(0.2)
except requests.RequestException as e:
logger.error(f"Request error: {e}")
time.sleep(5)
return all_candles
# Backfill 7 days of BTCUSDT 1m klines
if __name__ == "__main__":
now_ms = int(time.time() * 1000)
seven_days_ago = now_ms - (7 * 24 * 60 * 60 * 1000)
candles = backfill_klines("BTCUSDT", "1m", seven_days_ago, now_ms, limit_per_req=1000)
print(f"Total candles collected: {len(candles)}")
粘性会话的关键优势在于:同一 IP 的权重预算可以被连续的分页请求复用,避免了每请求轮换时的权重浪费。当 X-MBX-USED-WEIGHT-1M 接近上限或收到 429 时,切换到新的 session ID 即可获得新的 IP 和新的权重预算。
Node.js 实现:axios + ProxyHat 代理
以下是 Node.js 版本,使用 axios 通过 ProxyHat 代理抓取 24 小时行情数据,包含重试和并发控制。
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
const PROXYHAT_USER = 'your_username';
const PROXYHAT_PASS = 'your_password';
const PROXYHAT_GATE = 'gate.proxyhat.com';
const PROXYHAT_PORT = 8080;
const BINANCE_BASE = 'https://api.binance.com';
/**
* Build ProxyHat proxy URL with optional geo-targeting and sticky session.
* @param {string|null} country - ISO country code (e.g. 'US', 'DE')
* @param {string|null} sessionId - Sticky session identifier
* @returns {string} Full proxy URL
*/
function buildProxyUrl(country = null, sessionId = null) {
let username = PROXYHAT_USER;
if (country) username += `-country-${country}`;
if (sessionId) username += `-session-${sessionId}`;
return `http://${username}:${PROXYHAT_PASS}@${PROXYHAT_GATE}:${PROXYHAT_PORT}`;
}
/**
* Fetch 24hr ticker with IP rotation, retries, and weight tracking.
* @param {string} symbol - Trading pair, e.g. 'BTCUSDT'
* @param {number} maxRetries - Max retry attempts
* @returns {Promise<object>} Ticker data
*/
async function fetch24hrTicker(symbol, maxRetries = 5) {
const url = `${BINANCE_BASE}/api/v3/ticker/24hr`;
const params = { symbol };
for (let attempt = 0; attempt < maxRetries; attempt++) {
// Fresh residential IP per request
const proxyUrl = buildProxyUrl();
const agent = new HttpsProxyAgent(proxyUrl);
try {
const resp = await axios.get(url, {
params,
httpsAgent: agent,
timeout: 15000,
validateStatus: (s) => s < 500 // don't throw on 4xx
});
const weight = resp.headers['x-mbx-used-weight-1m'];
if (weight) {
console.log(`[Weight: ${weight}] Status: ${resp.status} | ${symbol}`);
}
if (resp.status === 200) {
return resp.data;
}
if (resp.status === 429) {
const retryAfter = parseInt(resp.headers['retry-after'] || '60', 10);
console.warn(`429 — sleeping ${retryAfter}s`);
await sleep(retryAfter * 1000);
continue;
}
if (resp.status === 418) {
const retryAfter = parseInt(resp.headers['retry-after'] || '120', 10);
console.error(`418 IP banned — sleeping ${retryAfter}s`);
await sleep(retryAfter * 1000);
continue;
}
if (resp.status === 451) {
console.warn('451 geo-blocked — retrying with US exit');
const usProxy = buildProxyUrl('US');
const usAgent = new HttpsProxyAgent(usProxy);
const usResp = await axios.get(url, { params, httpsAgent: usAgent, timeout: 15000 });
if (usResp.status === 200) return usResp.data;
}
console.warn(`Unexpected status ${resp.status}: ${JSON.stringify(resp.data).slice(0, 200)}`);
} catch (err) {
console.warn(`Error attempt ${attempt + 1}: ${err.message}`);
}
// Exponential backoff with jitter
const backoff = Math.pow(2, attempt) * 1000 + Math.random() * 1500;
console.log(`Backing off ${(backoff / 1000).toFixed(1)}s...`);
await sleep(backoff);
}
throw new Error(`Failed after ${maxRetries} attempts for ${symbol}`);
}
/**
* Batch fetch tickers with concurrency limit.
* @param {string[]} symbols - Array of trading pairs
* @param {number} concurrency - Max concurrent requests
* @returns {Promise<Array>} Array of {symbol, data} or {symbol, error}
*/
async function batchFetchTickers(symbols, concurrency = 5) {
const results = [];
let index = 0;
async function worker() {
while (index < symbols.length) {
const i = index++;
const sym = symbols[i];
try {
const data = await fetch24hrTicker(sym);
results.push({ symbol: sym, data });
} catch (err) {
results.push({ symbol: sym, error: err.message });
}
}
}
// Launch concurrency-limited workers
await Promise.all(Array.from({ length: concurrency }, () => worker()));
return results;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Example usage
(async () => {
const symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'ADAUSDT',
'DOGEUSDT', 'XRPUSDT', 'DOTUSDT', 'MATICUSDT', 'LINKUSDT'];
const results = await batchFetchTickers(symbols, concurrency = 5);
for (const r of results) {
if (r.data) {
console.log(`${r.symbol}: price=${r.data.lastPrice} | vol=${r.data.volume} | change=${r.data.priceChangePercent}%`);
} else {
console.error(`${r.symbol}: FAILED — ${r.error}`);
}
}
})();
curl 命令行快速验证
在编写代码之前,用 curl 快速验证代理连通性和权重消耗:
# Basic klines request through ProxyHat residential proxy
curl -x http://your_username:your_password@gate.proxyhat.com:8080 \
"https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1m&limit=100" \
-D - # dump headers to see X-MBX-USED-WEIGHT-1M
# Depth with limit=1000 (weight=20) through US exit
curl -x http://your_username-country-US:your_password@gate.proxyhat.com:8080 \
"https://api.binance.com/api/v3/depth?symbol=BTCUSDT&limit=1000" \
-D -
# 24hr ticker for single symbol (weight=1)
curl -x http://your_username:your_password@gate.proxyhat.com:8080 \
"https://api.binance.com/api/v3/ticker/24hr?symbol=ETHUSDT" \
-D -
# Sticky session for paginated backfill
curl -x http://your_username-session-backfill001:your_password@gate.proxyhat.com:8080 \
"https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1m&startTime=1700000000000&limit=1000" \
-D -
WebSocket vs REST:何时选择流式数据
对于实时市场数据,Binance 提供 WebSocket 市场流(wss://stream.binance.com:9443),包括 kline、depth、trade 等频道。在以下场景中,WebSocket 优于 REST 轮询:
- 实时 K 线更新:
<symbol>@kline_<interval>频道每 250ms-2000ms 推送一次更新,无需重复请求 - 增量深度更新:
<symbol>@depth频道推送订单簿增量,避免每秒轮询/api/v3/depth消耗 20-50 权重 - 多交易对实时监控: 单个 WebSocket 连接可订阅多个频道,权重消耗为零
但 REST 仍然适用于:
- 历史数据回填(WebSocket 只提供实时数据)
- 一次性快照查询
- 需要精确控制请求时机的场景
最佳实践是混合使用:用 REST 回填历史数据,用 WebSocket 维护实时更新。两者可以结合代理使用——WebSocket 连接也通过 ProxyHat SOCKS5 代理(gate.proxyhat.com:1080)建立。
常见错误与边界情况
1. 忽略 X-MBX-USED-WEIGHT-1M 响应头
很多开发者只检查 HTTP 状态码,不追踪权重消耗。这会导致你在毫无预警的情况下触发 429。正确做法是每次请求后解析该响应头,当值超过 5000 时主动降速或切换 IP。
2. 高频轮询 /api/v3/depth
limit=5000 的深度请求消耗 50 权重。如果每 500ms 轮询一次,1 分钟内消耗 6000 权重——直接触顶。解决方案:使用 WebSocket @depth 频道,或降低轮询频率并使用多个 IP 分散权重。
3. 未处理 HTTP 451 地理限制
如果你的出口 IP 位于美国,访问 api.binance.com 可能返回 451。使用 -country-DE 或其他非美国地理定位可以避免此问题。反之,如需访问 Binance.US,使用 -country-US。
4. 重试风暴
收到 429 后立即重试会加剧限速。务必实现指数退避并尊重 Retry-After 头。在 418 封禁场景下,应切换到完全不同的 IP(新的 session ID)。
5. 并发不受控
无限制并发会导致代理连接池耗尽和目标服务器拒绝连接。使用信号量或连接池限制并发数——对于 Binance API,建议每 IP 不超过 5-10 并发请求。
生产级最佳实践
- 权重监控: 始终解析
X-MBX-USED-WEIGHT-1M,设置软阈值(如 5000)触发 IP 轮换 - 请求间隔: 即使使用代理,也保持 200-500ms 的基础间隔,避免突发流量
- 断路器模式: 连续 3 次 429/418 后,暂停该 session ID 5 分钟
- 日志记录: 记录每个请求的 IP、权重、状态码、延迟,用于事后分析和优化
- 数据验证: Binance 返回的 K 线数据是数组格式,索引含义需对照 官方文档(索引 0=开盘时间,1=开盘价,6=收盘时间等)
- 错误分类: 区分代理错误(网络层)和 API 错误(应用层),分别处理
ProxyHat 配置与内部资源
要开始使用 ProxyHat 住宅代理抓取 Binance 数据,请按以下步骤配置:
- 注册并获取 ProxyHat 凭证(用户名/密码)
- 选择住宅代理套餐——查看 定价页面
- 使用
gate.proxyhat.com:8080(HTTP)或gate.proxyhat.com:1080(SOCKS5)作为网关 - 在用户名中添加地理定位和会话参数
- 查阅 支持的代理位置 了解可用的国家/城市
更多使用场景请参考 网络爬虫用例 和 SERP 追踪用例。完整 API 文档请访问 ProxyHat 文档站。
关键要点总结
- Binance REST API 采用权重限速,每 IP 每分钟约 6000 权重;高权重端点(如 depth limit=5000 消耗 50 权重)可在 2 分钟内耗尽预算
- 通过 ProxyHat 住宅代理轮换 IP,将权重分散到多个出口地址,实现合规的大规模数据采集
- 始终解析
X-MBX-USED-WEIGHT-1M响应头,设置软阈值主动降速- 粘性会话(
-session-xxx)适合分页回填,逐请求轮换适合高并发抓取- 实时数据优先使用 WebSocket 流,历史数据回填使用 REST + 代理轮换
- 对 429/418/451 状态码实施差异化处理策略,避免重试风暴
常见问题
使用代理抓取 Binance REST API 是什么意思?
指通过 HTTP/SOCKS5 代理服务器向 Binance 公开 REST API(如 /api/v3/klines、/api/v3/depth 等端点)发送请求,以轮换出口 IP 地址的方式分散 Binance 的权重限速。Binance 对每个 IP 每分钟限制约 6000 权重,超限返回 429,持续违规返回 418 封禁。使用住宅代理可以让每个请求从不同 IP 发出,从而将权重预算乘以 IP 数量,实现规模化采集。
为什么代理用户需要关注 Binance REST API 抓取?
Binance 的权重限速是按 IP 计算的,单 IP 采集深度数据(limit=5000,权重 50)在 2 分钟内就会触顶。代理用户通过轮换住宅 IP 可以将权重分散到多个地址,避免 429/418 封禁,同时通过地理定位(如 -country-US)绕过 Binance.com 对美国地区的 451 限制。这使得大规模历史数据回填和实时多交易对监控成为可能。
哪种代理类型最适合 Binance REST API 抓取?
住宅代理是最佳选择。Binance 的反爬系统对数据中心 IP 更敏感,容易触发更严格的限速或封禁。住宅代理使用真实 ISP 分配的 IP 地址,看起来像普通用户流量,更不容易被标记。ProxyHat 住宅代理通过 gate.proxyhat.com:8080(HTTP)或 :1080(SOCKS5)提供,支持国家/城市级地理定位和粘性会话,适合 Binance 的权重限速场景。
如何避免 Binance REST API 抓取时的 IP 封禁?
关键策略包括:始终监控 X-MBX-USED-WEIGHT-1M 响应头,在达到 5000 时切换 IP;对 429 响应立即降速并尊重 Retry-After 头;使用住宅代理逐请求轮换 IP;避免高频轮询高权重端点(如 depth limit=5000);实现指数退避重试而非立即重试;对 418 封禁切换到全新的 session ID 和 IP。混合使用 WebSocket(实时数据,零权重消耗)和 REST(历史回填)可以进一步降低封禁风险。
Binance WebSocket 和 REST API 哪个更适合数据采集?
取决于场景。WebSocket 适合实时数据——kline、depth、trade 频道推送更新不消耗 REST 权重,单连接可订阅多个频道。REST 适合历史数据回填和一次性快照查询。最佳实践是混合使用:REST + 代理轮填回填历史数据,WebSocket 维护实时更新。WebSocket 连接也可通过 ProxyHat SOCKS5 代理(gate.proxyhat.com:1080)建立。






