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

调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 17:13:09