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

如何使用Python从无响应体的HTTP响应中下载Excel附件

核心问题

你当前代码拿不到有效Excel文件有两个明显错误:

  • 给GET请求错误设置了Content-Type: application/json头,这个头是用来声明请求体格式的,GET请求无请求体时不需要设置,错误设置会导致服务端按JSON接口逻辑返回而非文件流
  • 读取响应后直接用utf-8解码内容,Excel是二进制格式文件,不能按文本编码解析,直接解码会导致内容损坏,你当前拿到空响应也和服务端识别请求格式错误直接返回空有关

推荐实现方案(requests库)

原生http.client处理流、重定向、头解析的逻辑比较繁琐,优先用更稳定的requests库实现,先执行安装命令:
pip install requests

完整代码如下:

import requests
import os
from urllib.parse import unquote

# 替换为你的实际配置
HOSTNAME = "目标站点域名,例如 bi.example.com"
EXPORT_API = "/dbapi/v4/sql_jobs/export/xlsx/1656366248381_479368648"
SAVE_FOLDER = "./excel_downloads"

# 自动创建本地保存目录
os.makedirs(SAVE_FOLDER, exist_ok=True)

headers = {
    # 删除错误的content-type配置
    "Authorization": f"Bearer {getToken()}",
    # 明确告知服务端需要接收Excel格式文件
    "Accept": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
}

# 开启流模式、自动跟随重定向
resp = requests.get(
    url=f"https://{HOSTNAME}{EXPORT_API}",
    headers=headers,
    stream=True,
    allow_redirects=True
)
# 请求失败直接抛错,避免拿到异常响应写坏文件
resp.raise_for_status()

# 从响应头自动解析文件名
file_name = "export.xlsx"  # 默认兜底文件名
content_disp = resp.headers.get("Content-Disposition")
if content_disp:
    if "filename*=" in content_disp:
        file_name = unquote(content_disp.split("filename*=")[1].split("'")[-1])
    elif "filename=" in content_disp:
        file_name = unquote(content_disp.split("filename=")[1].strip('"'))

# 二进制分段写入文件,避免大文件占满内存
save_path = os.path.join(SAVE_FOLDER, file_name)
with open(save_path, "wb") as f:
    for chunk in resp.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)

print(f"文件下载完成,保存路径:{save_path}")

原生http.client版本(无第三方依赖)

如果不能安装第三方库,可以用修正后的原生模块实现:

import http.client
import os
from urllib.parse import unquote

# 替换为实际配置
HOSTNAME = "目标站点域名"
EXPORT_API = "/dbapi/v4/sql_jobs/export/xlsx/1656366248381_479368648"
SAVE_FOLDER = "./excel_downloads"
os.makedirs(SAVE_FOLDER, exist_ok=True)

conn = http.client.HTTPSConnection(HOSTNAME)
headers = {
    "Authorization": f"Bearer {getToken()}",
    "Accept": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
}

conn.request("GET", EXPORT_API, headers=headers)
res = conn.getresponse()

# 校验状态码
if res.status != 200:
    conn.close()
    raise Exception(f"请求失败,状态码:{res.status},错误信息:{res.reason}")

# 解析文件名
file_name = "export.xlsx"
content_disp = res.getheader("Content-Disposition")
if content_disp:
    if "filename*=" in content_disp:
        file_name = unquote(content_disp.split("filename*=")[1].split("'")[-1])
    elif "filename=" in content_disp:
        file_name = unquote(content_disp.split("filename=")[1].strip('"'))

# 直接读取二进制内容,不要做utf-8解码
file_data = res.read()
save_path = os.path.join(SAVE_FOLDER, file_name)
with open(save_path, "wb") as f:
    f.write(file_data)

conn.close()
print(f"文件下载完成,保存路径:{save_path}")

常见问题排查
  • 如果改完代码还是拿到0KB空文件,先检查token是否有效、当前账号是否有该导出任务的下载权限
  • 部分站点会校验请求来源,可以在headers里加上Referer字段,值为导出按钮所在的页面地址
  • 确认导出任务已经执行完成:异步导出类接口如果任务还在运行中,不会直接返回文件流,可能返回空或者任务状态提示
  • 可以先打印响应头的Content-Type字段,如果返回值是application/json说明请求还是没被服务端识别为下载请求,需要核对接口路径、请求参数是否正确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:51:20