调用MEXC API获取加密货币历史数据时出现SSLError DECRYPTION_FAILED_OR_BAD_RECORD_MAC错误的原因及解决方法咨询
调用MEXC API获取加密货币历史数据时出现SSLError DECRYPTION_FAILED_OR_BAD_RECORD_MAC错误的原因及解决方法咨询
你好,我正在用Python结合MEXC API获取加密货币的历史数据,代码在运行100到300次迭代后总会触发一个SSL错误,哪怕把迭代次数限制到5次,有时候也会出现同样的问题。
我的示例代码
import requests from datetime import datetime, timezone from pandas import DataFrame, concat, date_range base_endpoint = r"https://api.mexc.com" def interval_range( start=datetime.fromtimestamp(0, timezone.utc), end=datetime.now(timezone.utc).replace(second=0, microsecond=0), timeframe: str = "1m", ): freq = "" if timeframe == "1m": freq = "16h" elif timeframe == "5m": freq = "83h" elif timeframe == "15m": freq = "250h" elif timeframe == "30m": freq = "500h" elif timeframe == "60m": freq = "1000h" elif timeframe == "4h": freq = "4000h" elif timeframe == "1d": freq = "24000h" elif timeframe == "1W": freq = "168000h" elif timeframe == "1M": freq = "1000MS" intervals = date_range(start, end, freq=freq).to_pydatetime().tolist() intervals.append(end) return [(intervals[i], intervals[i + 1]) for i in range(len(intervals) - 1)] def get_historical_data( symbol: str, timeframe: str = "1m", start: datetime = datetime.fromtimestamp(0, timezone.utc), end: datetime = datetime.now(timezone.utc).replace(second=0, microsecond=0), ): endpoint = base_endpoint + r"/api/v3/klines" intervals = interval_range(start, end, timeframe) intervals.reverse() df = None columns = [ "open timestamp", "open", "high", "low", "close", "volume", "close timestamp", "quote asset volume", ] for index, interval in enumerate(intervals): print(f"<{index} of {len(intervals)}>") payload = { "symbol": symbol, "interval": timeframe, "startTime": int(interval[0].timestamp() * 1000), "endTime": int(interval[1].timestamp() * 1000), "limit": 1000, } response = requests.get(url=endpoint, params=payload, timeout=30) if response.status_code == 200: new_df = DataFrame(response.json(), columns=columns) if df is None: df = new_df else: df = concat([df, new_df]) else: raise f"Error [{response.status_code}]: {response.json()['msg']}" df = df.sort_values("open timestamp") df = df[~df.duplicated(keep="last")] df = df.reset_index(drop=True) return df btcusdt_df = get_historical_data("BTCUSDT") btcusdt_df.to_csv("BTCUSDT.csv")
错误输出
<0 of 30127> <1 of 30127> <2 of 30127> <3 of 30127> ... <128 of 30127> <129 of 30127> <130 of 30127> <131 of 30127> <132 of 30127> Traceback (most recent call last): File "venv\Lib\site-packages\urllib3\connectionpool.py", line 464, in _make_request self._validate_conn(conn) ~~~~~~~~~~~~~~~~~~~^^^^^^ File "venv\Lib\site-packages\urllib3\connectionpool.py", line 1093, in _validate_conn conn.connect() ~~~~~~~~~~~~^^ File "venv\Lib\site-packages\urllib3\connection.py", line 741, in connect sock_and_verified = _ssl_wrap_socket_and_match_hostname( sock=sock, ...<14 lines>... assert_fingerprint=self.assert_fingerprint, ) File "venv\Lib\site-packages\urllib3\connection.py", line 920, in _ssl_wrap_socket_and_match_hostname ssl_sock = ssl_wrap_socket( sock=sock, ...<8 lines>... tls_in_tls=tls_in_tls, ) File "venv\Lib\site-packages\urllib3\util\ssl_.py", line 460, in ssl_wrap_socket ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls, server_hostname) File "venv\Lib\site-packages\urllib3\util\ssl_.py", line 504, in _ssl_wrap_socket_impl return ssl_context.wrap_socket(sock, server_hostname=server_hostname) ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "Python\Python313\Lib\ssl.py", line 455, in wrap_socket return self.sslsocket_class._create( ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ sock=sock, ^^^^^^^^^^ ...<5 lines>... session=session ^^^^^^^^^^^^^^^ ) ^ File "Python\Python313\Lib\ssl.py", line 1076, in _create self.do_handshake() ~~~~~~~~~~~~~~~~~^^ File "Python\Python313\Lib\ssl.py", line 1372, in do_handshake self._sslobj.do_handshake() ~~~~~~~~~~~~~~~~~~~~~~~~~^^ ssl.SSLError: [SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:1020) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "venv\Lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen response = self._make_request( conn, ...<10 lines>... **response_kw, ) File "venv\Lib\site-packages\urllib3\connectionpool.py", line 488, in _make_request raise new_e urllib3.exceptions.SSLError: [SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:1020) The above exception was the direct cause of the following exception: Traceback (most recent call last): File "venv\Lib\site-packages\requests\adapters.py", line 667, in send resp = conn.urlopen( method=request.method, ...<9 lines>... chunked=chunked, ) File "venv\Lib\site-packages\urllib3\connectionpool.py", line 841, in urlopen retries = retries.increment( method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2] ) File "venv\Lib\site-packages\urllib3\util\retry.py", line 519, in increment raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='api.mexc.com', port=443): Max retries exceeded with url: /api/v3/klines?symbol=BTCUSDT&interval=1m&startTime=1732204800000&endTime=1732262400000&limit=1000 (Caused by SSLError(SSLError(1, '[SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:1020)'))) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "test.py", line 81, in <module> btcusdt_df = get_historical_data("BTCUSDT") File "test.py", line 66, in get_historical_data response = requests.get(url=endpoint, params=payload, timeout=30) File "venv\Lib\site-packages\requests\api.py", line 73, in get return request("get", url, params=params, **kwargs) ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "venv\Lib\site-packages\requests\sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) File "venv\Lib\site-packages\requests\sessions.py", line 703, in send r = adapter.send(request, **kwargs) File "venv\Lib\site-packages\requests\adapters.py", line 698, in send raise SSLError(e, request=request) requests.exceptions.SSLError: HTTPSConnectionPool(host='api.mexc.com', port=443): Max retries exceeded with url: /api/v3/klines?symbol=BTCUSDT&interval=1m&startTime=1732204800000&endTime=1732262400000&limit=1000 (Caused by SSLError(SSLError(1, '[SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:1020)')))
请问这个错误是什么原因导致的?最有效的解决方法是什么?
错误原因分析
这个[SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC]错误通常和以下几个因素有关:
- 频繁请求触发服务器防护:你的代码生成了3万多个请求区间,短时间内大量请求会触发MEXC的反爬机制,服务器会主动中断SSL连接来限制请求频率。
- SSL连接复用异常:
requests默认会复用TCP连接,但在高频请求场景下,复用的连接可能出现SSL握手状态异常,导致解密失败。 - 网络环境波动:本地网络不稳定,导致SSL握手过程中数据包丢失或损坏,引发解密校验失败。
解决方法
这里有几个针对性的解决方案,你可以依次尝试:
1. 添加请求延迟,遵守API速率限制
MEXC公开API有明确的速率限制(一般为每分钟60次左右),在每次请求后添加延迟可以避免触发防护:
import time # 在get_historical_data函数的请求后添加延迟 response = requests.get(url=endpoint, params=payload, timeout=30) time.sleep(1) # 可根据实际情况调整为0.5-2秒
2. 禁用连接复用,每次请求新建连接
修改requests会话配置,禁用连接复用,避免旧连接的SSL异常:
# 在get_historical_data函数内创建会话时配置 session = requests.Session() adapter = requests.adapters.HTTPAdapter(pool_connections=1, pool_maxsize=1) session.mount('https://', adapter) # 用session.get代替requests.get response = session.get(url=endpoint, params=payload, timeout=30)
3. 添加SSL错误重试机制
使用urllib3的重试工具,自动处理SSL错误和请求失败:
from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry # 在get_historical_data函数开头配置重试策略 session = requests.Session() retry_strategy = Retry( total=3, # 总重试次数 backoff_factor=1, # 重试间隔指数增长(1s, 2s, 4s...) allowed_methods=["GET"], status_forcelist=[429, 500, 502, 503, 504], retry_on_ssl_errors=True # 开启SSL错误重试 ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount('https://', adapter) # 使用session发起请求 response = session.get(url=endpoint, params=payload, timeout=30)
4. 优化请求区间,减少请求次数
你的interval_range函数生成了过多请求区间(比如1m timeframe下3万多次),可以参考MEXC API文档调整时间区间的跨度,比如将1m的freq调整为更大的值(比如24h),减少总请求数。
5. 更新本地SSL相关依赖
确保你的Python环境中SSL相关库是最新版本,避免兼容性问题:
pip install --upgrade requests urllib3
额外建议
- 查看MEXC官方API文档,确认历史K线接口支持的最大时间范围,一次性获取更多数据,减少请求次数。
- 可以加入请求失败的捕获逻辑,避免程序直接崩溃,比如在
else分支添加重试或日志记录。
备注:内容来源于stack exchange,提问作者Aria AAs
相关产品推荐
相关产品推荐

