使用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.JSONDecoder的raw_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




