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

向JSON API发送GET请求偶尔返回空白响应的问题排查

问题

我用Python写了个爬虫,请求https://www.saltybet.com/state.json接口获取JSON内容,代码如下:

import requests
import time


class SaltyJson():
    def __init__(self):
        self.url = "https://www.saltybet.com/state.json"
        self.session = requests.Session()

    def get_json(self):
        try:
            self.response = self.session.get(self.url,
                                             headers={"User-Agent": "Mozilla/5.0", "Accept": "application/json"})
            if self.response.status_code != 200:
                time.sleep(1)
                self.get_json()
            else:
                return self.response.json()
        except requests.exceptions.ConnectionError:
            time.sleep(1)
            self.get_json()
        except requests.exceptions.JSONDecodeError:
            time.sleep(1)
            self.get_json()

初始请求正常,但运行数小时后经常出现requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)错误,此时self.response.json()返回None,也就是收到了空白响应,而且这种情况在服务器返回200状态码时也会发生。

这个问题是偶发性的,大部分同类错误是因为JSON无效、缺少请求头或服务器返回错误码,但我的请求99%都正常,只有偶尔会收到空白响应且状态码为200。排查后发现错误出在char 0位置,说明是空白响应或者完全无效的JSON;捕获异常后返回的None在访问键值时会触发TypeError: 'NoneType' object is not subscriptable,调试显示整个字典都是None。

我已经尝试的解决措施:

  • 添加指数退避机制排查服务器限流问题
  • 增加200状态码检查
  • 验证返回JSON的键数量是否符合预期(len(list(self.response.json().keys())) == 8)

另外,我发现该接口响应的Content-Type是text/html,不是预期的application/json,就算请求里带了"Accept":"application/json"头也没改变;但网站的JS代码里指定了contentType: "application/json; charset=utf-8"。

我的疑问:

  1. GET请求的请求头设置是否有问题,导致偶尔返回空白响应?这个问题和响应Content-Type为text/html有关吗?
  2. 如果确实是空白响应,除了状态码检查外,还需要添加哪些代码检查?

回答

关于请求头与Content-Type的问题

首先,请求头的设置存在优化空间,但空白响应的核心原因大概率不是Content-Type为text/html——毕竟你99%的请求都能正常解析JSON,说明服务器返回的内容实际是JSON格式,只是Content-Type标错了。

可以补充更贴近真实浏览器的请求头,降低被服务器识别为爬虫的概率,比如:

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": "application/json",
    "Accept-Language": "en-US,en;q=0.9",
    "Referer": "https://www.saltybet.com/",
    "Cache-Control": "no-cache"
}

服务器偶尔返回空白200响应,更可能是它的限流机制触发、临时负载过高或者连接中断导致的不完整响应,和Content-Type的关系不大。

针对空白响应的额外检查

除了状态码,还需要做这些关键检查:

  1. 检查响应内容长度:直接判断len(self.response.content) == 0,如果成立就是空白响应,触发重试
  2. 先解码再检查有效性:不要直接调用response.json(),先把响应内容解码成字符串并去除首尾空白,确认非空后再解析:
raw_content = self.response.content.decode('utf-8').strip()
if not raw_content:
    # 空白响应,进入重试逻辑
    pass
  1. 修复递归重试的返回逻辑:你当前代码里递归调用self.get_json()时没有写return,导致异常情况下递归的结果不会被返回,最终还是会得到None。所有递归调用都要加上return。

优化后的完整代码示例

import requests
import time
import json


class SaltyJson():
    def __init__(self):
        self.url = "https://www.saltybet.com/state.json"
        self.session = requests.Session()
        self.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": "application/json",
            "Accept-Language": "en-US,en;q=0.9",
            "Referer": "https://www.saltybet.com/",
            "Cache-Control": "no-cache"
        }
        self.retry_count = 0
        self.max_retries = 5

    def get_json(self):
        try:
            self.response = self.session.get(self.url, headers=self.headers)
            self.response.raise_for_status()  # 主动抛出4xx/5xx类错误
            
            # 检查内容是否为空
            if len(self.response.content) == 0:
                raise ValueError("Empty response received")
            
            # 解码并验证非空
            raw_content = self.response.content.decode('utf-8').strip()
            if not raw_content:
                raise ValueError("Blank decoded content")
            
            # 解析JSON并验证结构
            data = json.loads(raw_content)
            if len(list(data.keys())) != 8:
                raise ValueError("Invalid JSON structure")
            
            # 重置重试计数
            self.retry_count = 0
            return data
        
        except (requests.exceptions.RequestException, ValueError, json.JSONDecodeError):
            if self.retry_count >= self.max_retries:
                self.retry_count = 0
                return None
            
            # 指数退避重试
            delay = 2 ** self.retry_count
            time.sleep(delay)
            self.retry_count += 1
            return self.get_json()

内容的提问来源于stack exchange,提问作者Brian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:47:47