坚果云文件下载
使用说明
该脚本用于从坚果云的WebDAV服务中递归下载指定目录及其所有内容,并保存到本地。脚本通过发送PROPFIND
请求获取目录结构,然后下载所有文件。用户需要在脚本中配置坚果云的账号、密码、起始路径以及本地保存路径,并且如果超过了坚果云的api 限制会继续请求,并且保存的文件路径和名称和坚果云一致。
个人博客
个人博客直达地址
网站不断完善中里面拥有大量的脚本,并且源码完全开放 欢迎纯白嫖。关注公众私信可免费写脚本
WebDAV获取账号密码的方式
坚果云限制
-
坚果云API请求限制:
- 坚果云api 接口每半小时只能请求1500次
-
坚果云下载量限制:
- 坚果云每个月下载的文件大小不能超过2T的数据,如果操作只能联系客服进行重置。
配置步骤
-
修改脚本中的配置:
username
: 你的坚果云账号。password
: 你的坚果云WebDAV授权密钥。base_url
: 需要下载的坚果云文件夹路径。download_dir
: 文件下载保存的本地路径。
-
运行脚本:
- 确保已经安装所需的Python库:
requests
,logging
,xml.etree.ElementTree
,os
,time
,base64
。 - 在终端中运行脚本,脚本会自动递归下载指定目录中的所有文件。
- 确保已经安装所需的Python库:
-
日志记录:
- 脚本将下载过程中的信息记录在
jianguoyun.log
日志文件中,便于排查错误。
- 脚本将下载过程中的信息记录在
注意事项
-
账户安全:
- 确保
username
和password
的安全,避免在公共场合或代码仓库中暴露这些敏感信息。 - 建议将脚本中的密码配置部分换成环境变量或安全存储服务来保护敏感信息。
- 确保
-
下载目录:
- 在运行脚本之前,确保
download_dir
目录存在且有足够的存储空间。
- 在运行脚本之前,确保
-
重试机制:
- 脚本包含重试机制,在网络连接不稳定或其他原因导致下载失败时,会自动重试。默认的重试机制是无限次重试,用户可以根据需求调整
max_retries
变量。
- 脚本包含重试机制,在网络连接不稳定或其他原因导致下载失败时,会自动重试。默认的重试机制是无限次重试,用户可以根据需求调整
-
递归下载:
- 脚本会递归下载指定目录下的所有文件和子目录,确保
download_dir
路径设置正确,避免误操作。
- 脚本会递归下载指定目录下的所有文件和子目录,确保
代码说明
-
认证与请求:
- 脚本使用HTTP基本认证方式,生成Base64编码的认证信息,通过
Authorization
头部发送给坚果云服务器。 propfind
函数发送PROPFIND
请求,用于获取目录和文件信息。
- 脚本使用HTTP基本认证方式,生成Base64编码的认证信息,通过
-
文件下载:
download_file
函数负责下载文件,并将其保存到本地指定路径。如果下载失败,脚本会进行重试。- 下载过程中的日志信息将记录在
jianguoyun.log
文件中,便于追踪下载状态和错误信息。
-
目录解析与递归下载:
parse_response
函数解析PROPFIND
请求的响应内容,提取文件和目录信息。recursive_download
函数递归处理目录及其内容,确保所有文件都被正确下载。
import os
import base64
import requests
from xml.etree import ElementTree
import time
import logging
# 配置
username = '自己的坚果云账号'
password = '自己的坚果云KEY'
url = 'https://dav.jianguoyun.com'
base_url = 'https://dav.jianguoyun.com/dav/需要下载的文件夹名称/' # 初始定义的起始路径
download_dir = './downloads/' # 自定义下载路径
# 设置日志记录
log_file = 'jianguoyun.log'
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.FileHandler(log_file), logging.StreamHandler()])
# 生成Base64编码的认证信息
auth_str = f"{username}:{password}"
auth_bytes = auth_str.encode('ascii')
auth_base64 = base64.b64encode(auth_bytes).decode('ascii')
headers = {
'Authorization': f'Basic {auth_base64}'
}
def propfind(url):
"""发送PROPFIND请求以获取目录和文件信息"""
response = requests.request('PROPFIND', url, headers=headers)
response.raise_for_status()
return response.text
def download_file(url, local_path):
"""下载文件,带重试机制"""
retry_count = 0
retry_interval = 60 # 初始重试间隔为1分钟
max_retries = float('inf') # 无限重试
while retry_count < max_retries:
try:
response = requests.get(url, headers=headers, stream=True)
response.raise_for_status()
os.makedirs(os.path.dirname(local_path), exist_ok=True)
with open(local_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)
logging.info(f"下载完成: {url}")
return
except requests.exceptions.RequestException as e:
retry_count += 1
if retry_count == 3:
retry_interval = 15 * 60 # 如果重试3次后仍失败,则等待15分钟
elif retry_count > 3:
retry_interval = 60 # 如果在等待20分钟后仍失败,则每分钟重试一次
logging.warning(f"下载失败 ({url}),重试 {retry_count}/{max_retries} 次: {e}")
time.sleep(retry_interval)
logging.error(f"下载失败且重试次数耗尽: {url}")
def parse_response(response_text):
"""解析PROPFIND响应,提取文件和目录信息"""
tree = ElementTree.fromstring(response_text)
namespaces = {'d': 'DAV:'}
for response in tree.findall('d:response', namespaces):
href = response.find('d:href', namespaces).text
displayname = response.find('.//d:displayname', namespaces).text
resourcetype = response.find('.//d:resourcetype', namespaces)
is_collection = resourcetype.find('d:collection', namespaces) is not None
yield href, displayname, is_collection
def recursive_download(base_url, current_url, local_path, visited):
"""递归下载目录及其内容"""
response_text = propfind(current_url)
logging.info(f"正在处理目录: {current_url}")
for href, displayname, is_collection in parse_response(response_text):
full_url = url.rstrip('/') + href
local_file_path = os.path.join(local_path, displayname)
if full_url in visited:
continue
visited.add(full_url)
logging.info(f"正在处理: {full_url}")
if is_collection:
recursive_download(base_url, full_url, local_file_path, visited)
else:
download_file(full_url, local_file_path)
if __name__ == '__main__':
visited_urls = set()
last_successful_url = base_url # 记录上次成功下载的URL
recursive_download(base_url, last_successful_url, download_dir, visited_urls)
logging.info(f"下载完成,文件保存在 {os.path.abspath(download_dir)}")