Python调用YouTube API获取评论时JSON响应损坏致解析失败
解决YouTube API响应中JSON字段被换行拆分导致解析失败的问题
问题根源
直接用原生Socket处理HTTPS请求时,大概率是以下两个原因导致JSON损坏:
- 分块传输编码未处理:YouTube API的响应默认采用分块传输,每个数据块开头有十六进制的大小标记,结尾带
\r\n,如果直接拼接所有接收的数据,这些标记和换行会混入JSON内容。 - 数据接收不完整/拼接错误:Socket的
recv()是按缓冲区返回数据,多次接收后如果没正确分离响应头和响应体,会把响应头的换行符错误带入JSON体,导致字段值被拆分。
具体修复方案
1. 正确处理HTTPS连接与请求
原生Socket直接连443端口传输的是明文,必须用SSL包装才能解密HTTPS数据,否则接收的是乱码,看似换行拆分实际是解密失败:
import socket import ssl import json # 创建Socket并包装SSL层 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssl_sock = ssl.wrap_socket(sock) ssl_sock.connect(("www.googleapis.com", 443)) # 构造符合规范的YouTube API GET请求 request = ( "GET /youtube/v3/commentThreads?part=snippet&videoId=你的视频ID&key=你的API密钥 HTTP/1.1\r\n" "Host: www.googleapis.com\r\n" "Connection: close\r\n" "\r\n" ) ssl_sock.send(request.encode("utf-8"))
2. 完整接收并解析HTTP响应
不要直接拼接所有recv()数据,先分离响应头和响应体,再处理分块编码:
def receive_and_parse_response(ssl_sock): response = b"" # 循环接收所有数据直到连接关闭 while True: chunk = ssl_sock.recv(4096) if not chunk: break response += chunk # 分离响应头和响应体(按HTTP标准分隔符\r\n\r\n拆分) header_split = response.find(b"\r\n\r\n") if header_split == -1: raise ValueError("无效的HTTP响应格式") headers = response[:header_split].decode("utf-8") body = response[header_split+4:] # 处理分块传输编码 if "Transfer-Encoding: chunked" in headers: decoded_body = b"" remaining_data = body while remaining_data: # 读取块大小(十六进制转十进制) size_end = remaining_data.find(b"\r\n") if size_end == -1: break chunk_size = int(remaining_data[:size_end], 16) if chunk_size == 0: break # 提取块内容并跳过结尾的\r\n decoded_body += remaining_data[size_end+2 : size_end+2+chunk_size] remaining_data = remaining_data[size_end+2+chunk_size+2:] return decoded_body.decode("utf-8") else: # 非分块响应直接解码返回 return body.decode("utf-8") # 获取处理后的响应体 response_body = receive_and_parse_response(ssl_sock) ssl_sock.close() # 解析JSON try: data = json.loads(response_body) print("JSON解析成功") except json.JSONDecodeError as e: print(f"解析失败:{e}") print("损坏的响应片段:", response_body[:500])
3. 容错处理(针对已出现的换行拆分)
如果仍有偶发的字段内换行,可以在解析前清除双引号内的多余换行:
import re # 匹配双引号内的换行并移除 cleaned_body = re.sub(r'(?<=")([^"]*)\n([^"]*)(?=")', r'\1\2', response_body) try: data = json.loads(cleaned_body) except json.JSONDecodeError as e: print(f"修复后仍解析失败:{e}")
更简便的替代方案
直接使用requests库,它会自动处理HTTPS、分块传输、数据拼接等所有细节,避免原生Socket的繁琐:
import requests url = "https://www.googleapis.com/youtube/v3/commentThreads" params = { "part": "snippet", "videoId": "你的视频ID", "key": "你的API密钥" } response = requests.get(url, params=params) data = response.json() print(data)
内容的提问来源于stack exchange,提问作者Bruno Augusto
相关产品推荐
相关产品推荐

