在日常工作中,我们经常会遇到需要从Yandex云盘下载大量文件的情况。比如做数据分析时需要获取公开数据集,或者运维工作中需要批量下载日志文件。手动一个个点击下载不仅效率低下,而且在服务器环境下根本无法操作。这就是为什么我们需要一个自动化解决方案。
我最近接手了一个项目,需要从50多个Yandex云盘链接下载数据集。如果手动操作,估计得花上一整天时间。但用Python脚本处理后,整个过程不到10分钟就完成了。这就是自动化带来的效率提升。
Yandex云盘的公开分享链接有个特点:它们都遵循固定的格式规则。比如典型的分享链接长这样:https://disk.yandex.com/d/一串随机字符。这种规律性正是我们可以利用的关键点。
首先确保你的系统已经安装了Python3。我推荐使用Python 3.6或更高版本,因为后续要用到的json和urllib库在这些版本中更稳定。在终端输入以下命令检查版本:
bash复制python3 --version
如果没有安装,可以用这个命令快速安装(以Ubuntu为例):
bash复制sudo apt update && sudo apt install python3
我们的脚本主要依赖Python标准库中的几个模块:
这些库都是Python自带的,不需要额外安装。不过为了确保脚本运行顺畅,建议先更新pip:
bash复制python3 -m pip install --upgrade pip
Yandex云盘提供了一个公开的API接口,格式如下:
code复制https://cloud-api.yandex.net:443/v1/disk/public/resources/download?public_key=加密后的URL
这个API的工作流程是这样的:
让我们仔细看看脚本的每个部分:
python复制#!/usr/bin/env python3
# -*- coding: utf-8 -*-
这两行是标准的Python脚本开头,指定解释器和编码方式。
python复制import os, sys, json
import urllib.parse as ul
导入必要的库,urllib.parse用于URL编码解码。
python复制sys.argv.append('.') if len(sys.argv) == 2 else None
这行代码确保即使只提供一个参数(URL),脚本也能正常运行,默认下载到当前目录。
python复制base_url = 'https://cloud-api.yandex.net:443/v1/disk/public/resources/download?public_key='
url = ul.quote_plus(sys.argv[1])
folder = sys.argv[2]
构造API请求URL,并对用户输入的分享链接进行URL编码。
python复制res = os.popen('wget -qO - {}{}'.format(base_url, url)).read()
json_res = json.loads(res)
使用wget获取API响应,并将返回的JSON字符串转换为Python字典。
python复制filename = ul.parse_qs(ul.urlparse(json_res['href']).query)['filename'][0]
os.system("wget '{}' -P '{}' -O '{}'".format(json_res['href'], folder, filename))
从API响应中解析出文件名,然后使用wget下载文件到指定目录。
实际工作中,我们经常需要批量下载多个文件。可以创建一个文本文件(比如links.txt),每行一个Yandex云盘链接,然后使用这个脚本处理:
bash复制while read link; do python3 yandex_dl.py "$link" ./downloads; done < links.txt
大文件下载可能会中断,我们可以给wget添加断点续传参数:
python复制os.system("wget -c '{}' -P '{}' -O '{}'".format(json_res['href'], folder, filename))
这里的-c参数就是断点续传的关键。
默认的wget命令是静默模式(-q),如果想看下载进度可以去掉这个参数:
python复制os.system("wget '{}' -P '{}' -O '{}'".format(json_res['href'], folder, filename))
在网络不稳定的环境中,可以设置超时时间(单位秒):
python复制os.system("wget --timeout=30 '{}' -P '{}' -O '{}'".format(json_res['href'], folder, filename))
如果遇到403错误,可能是以下原因:
解决方法:
有时下载的文件名会出现乱码,这是因为编码问题。可以在wget命令中添加:
python复制os.system("wget --restrict-file-names=nocontrol '{}' -P '{}' -O '{}'".format(json_res['href'], folder, filename))
对于超过2GB的文件,建议使用curl代替wget:
python复制os.system("curl -L -o '{}/{}' '{}'".format(folder, filename, json_res['href']))
为了方便排查问题,可以添加简单的日志记录:
python复制import logging
logging.basicConfig(filename='yandex_dl.log', level=logging.INFO)
try:
# 原有代码
logging.info(f"Successfully downloaded {filename}")
except Exception as e:
logging.error(f"Failed to download: {str(e)}")
要加速多个文件的下载,可以使用Python的threading模块:
python复制from threading import Thread
def download_task(link, folder):
# 下载逻辑
threads = []
for link in links:
t = Thread(target=download_task, args=(link, "./downloads"))
threads.append(t)
t.start()
for t in threads:
t.join()
使用tqdm库可以添加美观的进度条:
python复制from tqdm import tqdm
import requests
response = requests.get(json_res['href'], stream=True)
total_size = int(response.headers.get('content-length', 0))
with open(f"{folder}/{filename}", "wb") as f, tqdm(
total=total_size, unit='B', unit_scale=True
) as pbar:
for data in response.iter_content(chunk_size=1024):
f.write(data)
pbar.update(len(data))
重要文件下载后应该验证校验和:
python复制import hashlib
def get_file_sha256(filename):
sha256_hash = hashlib.sha256()
with open(filename,"rb") as f:
for byte_block in iter(lambda: f.read(4096),b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
file_hash = get_file_sha256(f"{folder}/{filename}")
print(f"SHA256: {file_hash}")
避免占用过多带宽,可以限制下载速度:
python复制os.system("wget --limit-rate=1m '{}' -P '{}' -O '{}'".format(json_res['href'], folder, filename))
这里的1m表示限制为1MB/s。
如果下载的是敏感文件,建议下载完成后立即修改权限:
python复制os.chmod(f"{folder}/{filename}", 0o600)
Yandex官方提供了命令行工具,安装方法:
bash复制sudo apt install yandex-disk
使用示例:
bash复制yandex-disk download https://disk.yandex.com/d/5VdqLARizmnj3Q
优点:官方支持,功能全面
缺点:需要额外安装,配置复杂
rclone是一个强大的云存储管理工具,支持Yandex Disk:
bash复制rclone copy yandex:remote_path local_path
优点:支持多种云存储,功能强大
缺点:学习曲线较陡
aria2是一个多协议下载工具:
bash复制aria2c https://disk.yandex.com/d/5VdqLARizmnj3Q
优点:支持多线程,下载速度快
缺点:需要额外处理API请求
我最近帮一个研究团队设置了一个自动化数据收集系统。他们每天需要从20个不同的Yandex云盘链接下载最新的研究数据。使用这个脚本配合cron定时任务,完全实现了无人值守的自动化下载。
关键配置:
bash复制0 3 * * * /usr/bin/python3 /path/to/yandex_dl.py https://disk.yandex.com/d/link1 /data/store
某公司使用Yandex云盘作为日志备份存储,需要定期从多个服务器下载日志文件进行分析。通过扩展脚本,实现了:
一个摄影团队使用这个脚本自动下载客户分享的原始照片。脚本被扩展为:
如果频繁下载同一个链接,可以缓存API响应减少请求:
python复制import pickle
cache_file = "yandex_api_cache.pkl"
try:
with open(cache_file, "rb") as f:
cache = pickle.load(f)
except:
cache = {}
if url in cache:
json_res = cache[url]
else:
# 正常请求API
cache[url] = json_res
with open(cache_file, "wb") as f:
pickle.dump(cache, f)
对于大批量下载,使用requests.Session可以显著提升性能:
python复制import requests
session = requests.Session()
def download_file(url, path):
with session.get(url, stream=True) as r:
with open(path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
使用asyncio和aiohttp可以实现异步下载:
python复制import asyncio
import aiohttp
async def download_file(session, url, path):
async with session.get(url) as response:
with open(path, 'wb') as f:
while True:
chunk = await response.content.read(1024)
if not chunk:
break
f.write(chunk)
async def main():
async with aiohttp.ClientSession() as session:
tasks = [download_file(session, url, path) for url, path in file_list]
await asyncio.gather(*tasks)
asyncio.run(main())
在Windows上运行需要做少量修改:
修改后的下载命令:
python复制os.system(f"powershell -Command \"Invoke-WebRequest -Uri '{json_res['href']}' -OutFile '{os.path.join(folder, filename)}'\"")
macOS上可能需要额外安装wget:
bash复制brew install wget
或者使用curl:
python复制os.system(f"curl -o '{os.path.join(folder, filename)}' '{json_res['href']}'")
为了兼容Python 2.7和3.x,可以添加版本检查:
python复制import sys
if sys.version_info[0] < 3:
import urllib as ul
else:
import urllib.parse as ul
网络不稳定时,自动重试很重要:
python复制import time
max_retries = 3
retry_delay = 5
for attempt in range(max_retries):
try:
# 下载代码
break
except Exception as e:
if attempt == max_retries - 1:
raise
time.sleep(retry_delay)
retry_delay *= 2
针对不同HTTP状态码采取不同策略:
python复制import http.client
try:
# 下载代码
except http.client.HTTPException as e:
if e.code == 404:
print("文件不存在")
elif e.code == 429:
print("请求过于频繁,稍后再试")
else:
raise
下载前检查磁盘空间:
python复制import shutil
total, used, free = shutil.disk_usage(folder)
if free < estimated_size:
print("磁盘空间不足")
sys.exit(1)
创建Dockerfile:
dockerfile复制FROM python:3.8-slim
RUN apt update && apt install -y wget
COPY yandex_dl.py /app/
WORKDIR /app
ENTRYPOINT ["python", "yandex_dl.py"]
构建和运行:
bash复制docker build -t yandex-dl .
docker run -v $(pwd)/downloads:/app yandex-dl https://disk.yandex.com/d/link
创建Deployment配置:
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: yandex-dl
spec:
replicas: 1
template:
spec:
containers:
- name: downloader
image: yandex-dl
args: ["https://disk.yandex.com/d/link"]
volumeMounts:
- mountPath: /app/downloads
name: download-volume
volumes:
- name: download-volume
hostPath:
path: /data/downloads
在容器中限制资源使用:
dockerfile复制docker run --memory="512m" --cpus="1" yandex-dl https://disk.yandex.com/d/link
python复制import tkinter as tk
from tkinter import filedialog
def start_download():
url = url_entry.get()
folder = folder_entry.get()
# 调用下载函数
root = tk.Tk()
tk.Label(root, text="Yandex链接:").pack()
url_entry = tk.Entry(root, width=50)
url_entry.pack()
tk.Label(root, text="保存目录:").pack()
folder_entry = tk.Entry(root, width=50)
folder_entry.pack()
tk.Button(root, text="浏览", command=lambda: folder_entry.insert(0, filedialog.askdirectory())).pack()
tk.Button(root, text="下载", command=start_download).pack()
root.mainloop()
使用pystray创建后台程序:
python复制import pystray
from PIL import Image
def on_quit():
icon.stop()
image = Image.open("icon.png")
menu = pystray.Menu(pystray.MenuItem("退出", on_quit))
icon = pystray.Icon("yandex_dl", image, "Yandex下载器", menu)
icon.run()
在GUI中显示下载进度:
python复制from tkinter import ttk
progress = ttk.Progressbar(root, orient="horizontal", length=300, mode="determinate")
progress.pack()
def update_progress(current, total):
progress["value"] = (current / total) * 100
root.update_idletasks()
将下载记录保存到SQLite数据库:
python复制import sqlite3
conn = sqlite3.connect("downloads.db")
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS downloads (url TEXT, path TEXT, timestamp DATETIME)")
def log_download(url, path):
cursor.execute("INSERT INTO downloads VALUES (?, ?, datetime('now'))", (url, path))
conn.commit()
对于需要认证的链接,可以扩展脚本支持OAuth:
python复制import requests_oauthlib
oauth = requests_oauthlib.OAuth2Session(
client_id="your_client_id",
token="your_token"
)
response = oauth.get(json_res["href"])
记录详细的操作日志:
python复制import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("yandex_dl")
handler = RotatingFileHandler("app.log", maxBytes=1e6, backupCount=5)
logger.addHandler(handler)
def download_file(url):
logger.info(f"开始下载: {url}")
try:
# 下载逻辑
logger.info(f"下载完成: {url}")
except Exception as e:
logger.error(f"下载失败: {str(e)}")