多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

服务器上直接下载Google Drive文件:curl与gdown实战指南

服务器上直接下载Google Drive文件:curl与gdown实战指南 1. 先说为什么要在服务器端直接处理 Google Drive 文件前阵子帮同事迁移项目数据服务器是只有 SSH 终端和 Linux 系统的环境没有图形桌面。同事给我两个 Google Drive 分享链接里面有模型权重、标注数据和几份配置文件。按我过去的习惯会先把这些文件下载到本地再压缩上传服务器。但这次数据总量超过 5GB来来回回倒腾时间和磁盘中转成本都太高直接在服务器上把 Google Drive 的数据拉下来就成了唯一合理的方案。顺着这个需求往下走我实际要做的事情是在纯文本环境下复刻浏览器里“点开分享链接再点下载”的全部逻辑。包括找到文件 ID、处理中间确认页、保持会话 Cookie、把数据落盘到指定目录。整篇文章基本就是这些步骤的展开。这篇文章适合三类读者一类是刚上手服务器运维、正在找“服务器上怎么直接下载网盘文件”方案的朋友一类是已经会一点 wget 和 curl但卡在大文件下载、确认 Token 和配额限制上的开发还有一类是把服务器当成临时数据中转站、希望把重复下载流程固化成脚本的进阶用户。后面的内容以 Linux 环境为例Windows 服务器也能参考同一个思路只是命令细节会有些差异。为什么我不推荐“先下载到本地再上传服务器”道理很简单本地到服务器这条链路的带宽通常是 10Mbps 到 100Mbps 不等5GB 数据就算带宽很理想也要等很久而且本地电脑中途断电、休眠、断网都会让任务前功尽弃。反过来服务器直连 Google Drive很多机房出口带宽比本地宽带大得多下载速度更快而且把任务放进 tmux 后哪怕本地电脑关机服务器上的下载也照常进行。这也是我在处理这类任务时坚持“能服务器直下的绝不在本地过一道手”的原因。2. 拆解分享链接结构文件 ID 从哪里来为什么第一次请求会被中间页拦截从浏览器打开一个 Google Drive 分享链接地址栏里通常长这样https://drive.google.com/file/d/1A2B3C4D5E6F7G8H9I0J/view?uspsharing如果分享的是整个文件夹地址会变成另一种形态https://drive.google.com/drive/folders/1A2B3C4D5E6F7G8H9I0J?uspsharing2.1 文件 ID 才是唯一需要关心的锚点对单个文件来说/d/和/view之间的那串字符就是 Google Drive 分配给这个文件的唯一 ID。这串 ID 通常由字母、数字和少量符号组成不区分大小写。拿到分享链接后第一件事就是把文件 ID 单独提取出来存成变量后续所有下载 URL 都靠它拼接。标准的下载地址模板长这样https://drive.google.com/uc?exportdownloadidFILEIDuc是 user content 的缩写exportdownload表示让服务端以附件形式输出。这个 URL 即使不带任何请求头也能访问但服务端返回给你的往往不是文件本身。2.2 为什么明明是下载链接返回的却是 HTML很多第一次在服务器上操作的同行都会疑惑地址已经带了exportdownload文件 ID 也没填错curl请求后拿到的却是一堆 HTML而不是压缩包二进制数据。这里要拆开讲一下 Google Drive 的下载机制。当文件体积较大或者服务端判断请求方可能是自动化脚本时它会插入一个“确认步骤”。这个步骤一方面拦截滥用下载的流量另一方面给病毒扫描结果留出决策空间。确认页里通常藏着一个confirm字段常见形态有两种一种是隐藏表单字段input typehidden nameconfirm valuetPdq0TXwRHxzeI.../另一种是带confirm参数的跳转链接a href/uc?exportdownloadconfirmtPdq0TXwRHxzeI...idFILEIDdownload/a只能把confirm的值传进下一次请求服务端才会真正放行文件数据。官方 SDK 以外的场景几乎都是靠识别这个参数来继续下载的。2.3 通过响应头判断当前返回的是文件还是页面不打开 HTML 内容也能判断请求是否成功。我用得最多的方法是用 curl 获取响应头只看关键字段curl -sI -L https://drive.google.com/uc?exportdownloadidFILEID -o /dev/null -w %{http_code} %{content_type}\n如果 Content-Type 返回的是text/html说明还在确认页阶段如果返回的是application/zip、application/pdf、application/octet-stream这类真实文件类型说明直链已经打通。另外Content-Length显示的大小如果和源文件体积对不上也说明数据还没有真正进入下载通道。2.4 User-Agent 要不要模拟浏览器默认的命令行 UA 和浏览器 UA在 Google Drive 的中间层策略里会有不同待遇。给请求加上一个常规浏览器 UA并不是在做什么额外操作只是让服务端认为是正常的浏览器访问下载链路会更稳定。我在实际测试中碰到过这样的情况默认 UA 状态下大文件下载到中途连接被快速终止换一个常见 Chrome UA 后整个下载过程就跑得顺畅多了。UA 示例Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36这个细节看起来很小但往往决定你能不能一次性把文件拉下来。3. 直接下载的经典方案两次请求提取 confirm 参数这一步是整套操作的核心。整体思路压缩成三个动作第一次请求拿确认参数第二次请求带上参数和 Cookie 下载文件最后对文件做完整性检查。3.1 第一步获取 confirm 参数把文件 ID 存在变量里后面多个命令复用不用反复粘贴长串字符FILEID1A2B3C4D5E6F7G8H9I0J UAMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 CONFIRM$(curl -s -L \ https://drive.google.com/uc?exportdownloadid${FILEID} \ -A $UA \ | grep -oP nameconfirm value\K[^] \ | head -n 1) echo CONFIRM$CONFIRM如果服务器上的 grep 不支持-P参数macOS 上的 BSD grep 就是个典型例子我一般换成 sed 版本CONFIRM$(curl -s -L https://drive.google.com/uc?exportdownloadid${FILEID} -A $UA \ | sed -n s/.*nameconfirm value\([^]*\).*/\1/p \ | head -n 1)这两种方式都能从确认页里把隐藏字段的值抠出来。如果命令执行后CONFIRM是空的大概率是页面结构有变化不是变量写错。这时可以先保存一份 HTML 到本地人工看一眼再决定下一步。3.2 第二步携带确认参数和 Cookie 完成下载拿到CONFIRM值以后容易被忽略的就是 Cookie。第一次请求时服务端会种下若干会话 Cookie第二次请求如果不带上这些 Cookie确认参数就是一段孤立的字符串服务端没法验证你是同一次浏览会话的延续仍然会把你拦截在确认页外面。所以我实际运行的下载命令是curl -L -c cookies.txt -b cookies.txt \ https://drive.google.com/uc?exportdownloadconfirm${CONFIRM}id${FILEID} \ -A $UA \ -o $HOME/data/model_weights.zip-c cookies.txt表示把服务端返回的新 Cookie 写入文件-b cookies.txt表示从文件读取 Cookie 作为请求头。注意先创建好目标目录比如$HOME/data不存在时curl 不会帮你自动建立。3.3 下载完成后的文件类型判断下载完成后最少要做两类检查。第一类是看文件真实类型file model_weights.zip如果输出的是Zip archive data或gzip compressed data说明二进制数据正常。如果输出的是HTML document说明你下载下来的其实是确认页赶紧回去检查 Cookie 或 confirm 参数。第二类是用压缩包自带的完整性检查unzip -t model_weights.zip这个检查能发现网络层的静默错误。比如下载中断但进程没有立即报错文件大小看着差不多解压时却报 CRC 失败的情况我就碰到过好几回。3.4 大文件场景下的断点续传中断和断线在跨机房下载里并不少见curl 的-C -参数可以实现断点续传curl -C - -L -b cookies.txt -c cookies.txt \ https://drive.google.com/uc?exportdownloadconfirm${CONFIRM}id${FILEID} \ -o $HOME/data/model_weights.zip断点续传的前提是服务端支持 Range 请求Google Drive 整体是支持的。但我要说一句实际体会Token 和 Cookie 挂太久之后服务端往往会判定会话失效续传时可能又回到确认页。所以我遇到大文件下载中断不会一直跟-C -较劲而是重新跑一遍完整脚本大多数时候反而更省时间。4. 文件夹分享或临时任务直接用 gdown 更务实如果临时要从 Google Drive 拉一个共享文件夹手动逐个提取文件 ID 是反人类的。这种情况我推荐直接用 gdown它对文件夹递归和身份识别都很友好。4.1 安装和典型命令gdown 是 Python 包安装一次全局生效pip3 install gdown下载单个文件时--fuzzy参数可以识别任意带 ID 的分享链接gdown --fuzzy https://drive.google.com/file/d/1A2B3C4D5E6F7G8H9I0J/view?uspsharing -O $HOME/data/weights.zip如果不加--fuzzy参数位置也可以直接填文件 IDgdown 1A2B3C4D5E6F7G8H9I0J -O $HOME/data/weights.zip文件夹下载用--foldergdown --folder https://drive.google.com/drive/folders/1A2B3C4D5E6F7G8H9I0J?uspsharing文件会按原目录结构保存到当前目录下。控制台会逐个打印每个文件的大小和保存路径实时看到进度比 curl 脚本更直观。4.2 什么时候选脚本什么时候选 gdown对比维度两段 curl 脚本gdown下载单个文件稳定依赖系统自带 curl方便命令简短文件夹递归需要自己按文件列表遍历原生支持Cookie 和确认 Token完全可控库内部自动处理定时任务容易集成到 shell需要额外保留 Python 依赖新环境部署无需安装任何东西需要 python3 和 pip如果只是偶尔下载两三个文件gdown 更省事。如果是定期从固定分享地址拉更新我更建议用 curl 脚本因为你能完全掌控每一步的重试逻辑和日志输出。4.3 大文件夹列表阶段为什么慢gdown 在下载文件夹时会先通过接口拉取整个目录的 JSON 文件列表再根据列表逐个下载。目录层级深、文件数量上千时第一阶段就会花费不少时间这属于正常现象。下载过程中如果个别文件触发了权限限制或配额gdown 不会中断整个任务而是那个文件报错后继续处理后面的文件。这个容错设计对长期备份任务很实用。另外gdown 还支持--continue参数可以跳过已存在的文件适合反复同步同一个文件夹。我的习惯是先跑一次全量之后再用gdown --continue --folder做增量拉取省了不少流量。5. 在真实服务器环境里踩过几次坑排查思路直接在服务器上拉网盘文件除了确认页还有不少环境因素干扰。下面这几类问题都是我实际排查中遇到过的按“现象 → 原因 → 处理”的方式整理出来。5.1 请求返回 HTML里面出现“临时服务器问题”字样这个提示字面意思像是 Google 临时故障但我的实践里更多和网络链路质量相关。比如服务器到 Google Drive 服务端的链路抖动某些路由节点不稳定请求可能被重置。排查办法先确认服务器基础连通性再模拟一次带浏览器 UA 的请求观察返回码和响应时间。如果反复出现换个时段请求成功率往往不一样。更深层的原因更多集中在权限层面。分享链接的权限如果是“知道链接的人可查看”直接下载没问题如果分享者把权限调成“仅指定用户可查看”命令行下载拿到的就是一个类似错误页的返回。出现这种情况时让分享者在浏览器里调整一下访问范围问题基本就解决了。5.2 请求返回“无法扫描病毒”之类的提示Google Drive 在下载环节有一个病毒扫描流程。压缩包里如果包含可执行文件比如模型推理工具里的二进制程序服务端可能返回“Google Drive 无法扫描此文件以检测病毒”以及“您确定要下载吗”的确认文案。处理办法是重新走确认步骤但这次直接在首次请求里把confirm参数写成t意为已知晓风险、继续下载curl -L https://drive.google.com/uc?exportdownloadconfirmtidFILEID -o file.zip这个confirmt在多篇社区记录里都有出现很多自动化工具内部也这么用。它适用于你明确知道文件来源可信、需要绕开交互提示的场景。5.3 显示配额超限或“访问次数过多”特征是页面文本包含 Google 官方的配额提示。很多用户会下意识以为是自己宽带的问题但实际很明确当分享链接太热门或者同一文件近期下载请求次数已经超过当日配额服务端会拒绝继续提供下载。这既跟你服务器所在网络无关也跟分享者账户状态有关。等几小时或者第二天再试通常能恢复。如果你能控制分享源可以复制一份文件到新文件夹用新文件 ID 重新下载往往能立即绕开原文件的配额。这个方法在我一次需要拉取 13GB 数据集时帮我快速解决了问题。5.4 404 或“文件不存在”提示先怀疑文件 ID 是否完整复制。分享链接里的 ID 一般很长我踩过一次最典型的坑是 Shell 变量里混进了不可见字符导致最终 URL 里的 ID 缺少了约 20 个字符。这种情况在服务器网络侧看不出任何异常返回状态也很干净但只要把本地变量和浏览器地址栏里的原文一比问题立刻暴露。排除了 ID 原因之后再看分享者是否移除了文件权限或者文件是否已被移入回收站。如果是权限调整浏览器里也会看到类似“您需要访问权限”的页面。5.5 下载到一半连接被重置大文件传输时最容易遇到这类问题。除开 TLS 层面的证书限制如果中间链路设备强制断开了空闲连接或者单次传输超过阈值curl 进程会直接收到错误退出。我的应对是在 curl 命令里主动设置超时和重试参数curl --connect-timeout 15 --max-time 3600 --retry 5 --retry-delay 5 -L \ -b cookies.txt -c cookies.txt \ https://drive.google.com/uc?exportdownloadconfirm${CONFIRM}id${FILEID} \ -A $UA -o file.zip--retry的作用是在连接中断时自动重新发起请求。注意它并不适用于所有错误码但应对传输层中断已经足够。6. 把下载流程固化成可复用脚本重复劳动最容易出错尤其是靠图形界面才能完成的下载动作。我把上面这套流程提取成了 Shell 脚本之后每次从服务器拉数据都走同一入口稳定且可记录。6.1 一个最小可用的脚本#!/usr/bin/env bash set -euo pipefail URL$1 OUTPUT$2 FILEID$(echo $URL | grep -o /d/[^/]* | cut -d / -f 3) if [ -z $FILEID ]; then echo [ERROR] 无法从链接中提取文件 ID exit 1 fi UAMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 CONFIRM$(curl -s -L https://drive.google.com/uc?exportdownloadid${FILEID} \ -A $UA \ | grep -oP nameconfirm value\K[^] \ | head -n 1) if [ -z $CONFIRM ]; then echo [WARN] 未获取到 confirm 参数尝试使用 confirmt CONFIRMt fi curl -L -b cookies.txt -c cookies.txt \ https://drive.google.com/uc?exportdownloadconfirm${CONFIRM}id${FILEID} \ -A $UA \ -o $OUTPUT echo [OK] 下载完成: $OUTPUT保存成gdrive_fetch.sh后给可执行权限即可使用chmod x gdrive_fetch.sh ./gdrive_fetch.sh https://drive.google.com/file/d/1A2B3C4D5E6F7G8H9I0J/view ./output.zip如果后续grep -oP在个别 Linux 发行版上不可用替换成前面提到的 sed 版本就行。脚本里我已经对CONFIRM为空的情况做了兜底不会一遇到页面结构变化就完全卡死。6.2 扩展加入本地文件大小判断对定时任务来说每次重新下载相同文件既不经济又消耗配额。我通常会先请求一次响应头取出Content-Length再和本地文件大小对比。如果一致直接跳过下载。SIZE$(curl -sI -L https://drive.google.com/uc?exportdownloadid${FILEID} \ -A $UA | grep -i content-length | tail -n 1 | awk {print $2} | tr -d \r) if [ -f $OUTPUT ] [ $(stat -c%s $OUTPUT) $SIZE ]; then echo [SKIP] 文件已存在且大小匹配 exit 0 fi这个方法不复杂却能明显减少重复网络请求。如果你要下载的文件数量很大建议用一个 CSV 清单驱动整个脚本批量运行每个文件一行便于追踪。6.3 扩展把关键信息写进日志上次遇到配额限制时为了分析问题我把第一次请求的响应体保存成了文件再用 grep 搜索错误关键词一下子就定位了原因。日常运维我建议把每个任务的日志写到统一目录包含文件 ID、下载时间、HTTP 状态、文件大小这些字段。后面不管是排查定时任务失败还是做带宽统计都有数据可查不用靠记忆复盘。7. 给新手的几条服务器操作建议整个下载流程到这里已经完整了。但放在真实环境里还有一些属于“服务器操作基础常识”的东西值得单独说一说。7.1 长任务优先放到持续会话里运行SSH 连接一旦断开前台运行的 curl 进程很可能收到挂断信号而中断。我习惯用tmux建一个独立会话跑下载任务本地电脑休眠、断网、关闭终端都不影响服务器上的进程tmux new -s download ./gdrive_fetch.sh https://drive.google.com/file/d/1A2B3C4D5E6F7G8H9I0J/view ./weights.zip按Ctrlb再按d可以脱离会话任务继续执行。之后随时用tmux attach -t download重新进入查看进度。7.2 磁盘余量要提前确认下载 20GB 文件时最崩溃的情况是磁盘在 17GB 处写满curl 报错才知道空间不够。我的习惯是开始前先看df -h /home du -sh /home/data最好留出比文件大小多 10% 以上的余量。因为一些下载工具还会生成临时文件空间打得过紧容易莫名其妙失败。7.3 服务器时间偏差可能影响 TLS 验证偶尔会遇到证书验证失败的报错服务器时间偏差是常见原因。排查方式很简单date如果时间和标准时间偏差很大用 chrony 或 ntpdate 同步一下就好。这类问题看起来跟下载无关但卡住的人不少放在这里当作提示。7.4 下载完成不等于拿到可用数据压缩包下载完毕用ls -lh看到大小和源文件一致也不代表一定没出问题。因为传输过程存在极小概率的静默错误解压阶段才暴露。养成解压后立即检查的习惯tar -tzf large_file.tar.gz /dev/null echo OK unzip -t weights.zip /dev/null echo OK等模型加载阶段才发现文件损坏再回头处理代价就要高得多。8. 最后补一句实操体会在服务器上直接下载 Google Drive 文件的事网上信息零散但完整跑通的人总得碰几次壁。我反复尝试后形成的默认路径是能用两次 curl 提取 Token 解决的单文件绝不多装工具遇到文件夹递归就交给 gdown 处理。无论用哪种方式核心都逃不开“文件 ID 确认 Token Cookie 会话”这三个要素。最后分享一个我从实际任务里悟出来的小技巧把两次请求的 Cookie 文件统一放在固定目录每个任务用独立文件名比如cookies_$(date %s).txt。这样做既避免会话残留串到别的下载任务又能保留现场证据排查问题时有据可查。掌握这套流程之后从服务器拉 Google Drive 数据基本能稳定在一分钟内进入下载状态不用再在浏览器和命令行之间来回折腾。
返回列表