You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

使用Python requests流式请求API时持续触发JSONDecodeError的解决方法

解决requests流式请求中的JSONDecodeError问题

嘿,我来帮你搞定这个头疼的JSON解析问题!你说得完全对——这个报错就是因为API返回的是一个完整的JSON数组,但你用iter_lines()的时候,会把这个数组拆成零碎的片段(比如单独的[{...},这类内容),单独解析这些片段当然会失败,它们根本不是有效的JSON嘛!

下面给你两种解决方案,根据你的需求选就行:

方案一:直接获取完整响应(推荐,除非数据量极大)

如果返回的数据量不是特别大,其实完全没必要用流式请求,直接把整个响应内容拿过来再解析就好,简单又靠谱:

import json
import requests

s = requests.Session()
payload = {'limit': 0}
# 去掉stream=True,直接获取完整响应内容
r = s.get('https://api.coinmarketcap.com/v1/ticker', params=payload, timeout=(connect_timeout, read_timeout))
r.raise_for_status()
# 一次性解析整个JSON数组
rsvps = json.loads(r.text)
# 遍历数组里的每个对象
for rsvp in rsvps:
    print(rsvp)

方案二:流式拼接完整JSON后解析(适合超大数据量)

如果确实需要流式处理(比如数据量太大,不想占太多内存),那你得自己把流式接收的内容拼接起来,直到拿到完整的JSON数组再解析。这里给你两种实现方式:

方式1:简单判断结尾的闭合括号

这种方法比较直观,假设API返回的是标准的JSON数组,我们一直拼接内容,直到拿到数组的闭合]

import json
import requests

s = requests.Session()
payload = {'limit': 0}
r = s.get('https://api.coinmarketcap.com/v1/ticker', params=payload, timeout=(connect_timeout, read_timeout), stream=True)
r.raise_for_status()

# 用buffer来拼接流式接收的内容
buffer = []
for chunk in r.iter_content(chunk_size=1024, decode_unicode=True):
    buffer.append(chunk)
    # 检查最后一块内容是否以数组闭合符结尾
    if buffer[-1].endswith(']'):
        break

# 拼接所有内容后解析
full_content = ''.join(buffer)
rsvps = json.loads(full_content)
for rsvp in rsvps:
    print(rsvp)

方式2:用JSONDecoder逐步解析(更严谨)

如果担心API返回的JSON格式有变动(比如闭合符不在最后一块的结尾),可以用json.JSONDecoderraw_decode方法来尝试逐步解析,直到成功拿到完整的JSON:

import json
import requests

s = requests.Session()
payload = {'limit': 0}
r = s.get('https://api.coinmarketcap.com/v1/ticker', params=payload, timeout=(connect_timeout, read_timeout), stream=True)
r.raise_for_status()

decoder = json.JSONDecoder()
buffer = ''
for chunk in r.iter_content(chunk_size=1024, decode_unicode=True):
    buffer += chunk
    try:
        # 尝试解析当前buffer中的JSON,返回解析结果和未解析的剩余内容索引
        result, index = decoder.raw_decode(buffer)
        # 解析成功,拿到完整的JSON数组
        rsvps = result
        # 可以处理剩余的内容(如果有的话,这里我们不需要就直接break)
        break
    except json.JSONDecodeError:
        # 解析失败,说明内容还不完整,继续接收下一块
        continue

for rsvp in rsvps:
    print(rsvp)

最后补充一句:你原来用iter_lines()的思路更适合那种每行都是一个独立JSON对象的流式响应(比如NDJSON格式),但CoinMarketCap的API返回的是一个完整的JSON数组,所以不适用这种方式哦!

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

火山引擎 最新活动