Created
June 19, 2025 01:40
-
-
Save aweffr/b02d656d072e249f580aea597354e512 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
import argparse | |
import datetime | |
import os | |
import sys | |
import requests | |
from urllib.parse import urlparse | |
def download_file(url: str, filename: str = None): | |
""" | |
Downloads a file from a given URL with a progress bar. | |
Args: | |
url (str): The URL of the file to download. | |
filename (str, optional): The path to save the file to. | |
If None, a timestamped filename is generated. | |
""" | |
# --- 1. 确定最终的文件名 --- | |
if filename is None: | |
try: | |
# 尝试从 URL 路径中提取扩展名 | |
path = urlparse(url).path | |
ext = os.path.splitext(path)[1] | |
# 如果 URL 类似 "https://.../resource" 没有扩展名,或查询参数中有 .mp3 | |
if not ext and '.mp3' in url: | |
ext = '.mp3' | |
# 如果还是没有,使用 .bin 作为通用二进制文件后缀 | |
if not ext: | |
ext = '.bin' | |
except Exception: | |
ext = '.mp3' # 最终回退到 .mp3,符合您最初的用例 | |
timestamp = datetime.datetime.now().strftime('%Y-%m-%dT%H-%M-%S') | |
output_filename = f"{timestamp}{ext}" | |
else: | |
output_filename = filename | |
# --- 2. 执行下载 --- | |
try: | |
print(f"URL: {url}") | |
print(f"Output: {output_filename}") | |
# 使用 stream=True 进行流式下载 | |
with requests.get(url, stream=True, allow_redirects=True, timeout=30) as r: | |
# 检查请求是否成功 (HTTP 状态码 200-299) | |
r.raise_for_status() | |
# 从响应头获取文件总大小 | |
total_size = int(r.headers.get('content-length', 0)) | |
# 设置块大小 (8KB) | |
chunk_size = 8192 | |
downloaded_size = 0 | |
print("Downloading...") | |
with open(output_filename, 'wb') as f: | |
for chunk in r.iter_content(chunk_size=chunk_size): | |
f.write(chunk) | |
downloaded_size += len(chunk) | |
# --- 3. 绘制进度条 --- | |
if total_size > 0: | |
# 计算进度 | |
progress = downloaded_size / total_size | |
bar_length = 40 | |
filled_length = int(bar_length * progress) | |
bar = '█' * filled_length + '-' * (bar_length - filled_length) | |
# 已下载和总大小的可读格式 (MB) | |
downloaded_mb = downloaded_size / (1024 * 1024) | |
total_mb = total_size / (1024 * 1024) | |
# 打印到同一行 | |
sys.stdout.write(f"\rProgress: [{bar}] {progress:.1%} ({downloaded_mb:.2f}MB / {total_mb:.2f}MB)") | |
sys.stdout.flush() | |
# 下载完成,换行 | |
print("\n" + "="*50) | |
print(f"Downloaded '{output_filename}' successfully.") | |
print("="*50) | |
# --- 4. 错误处理 --- | |
except requests.exceptions.HTTPError as e: | |
print(f"\nError: HTTP request failed with status code {e.response.status_code}") | |
print(f"URL: {e.request.url}") | |
except requests.exceptions.RequestException as e: | |
print(f"\nError: A network-related error occurred: {e}") | |
except KeyboardInterrupt: | |
print("\nDownload cancelled by user. Deleting partial file.") | |
if os.path.exists(output_filename): | |
os.remove(output_filename) # 删除未下载完的片段文件 | |
sys.exit(1) | |
except Exception as e: | |
print(f"\nAn unexpected error occurred: {e}") | |
def main(): | |
"""主函数,处理命令行参数""" | |
parser = argparse.ArgumentParser( | |
description="A simple script to download a file from a URL with a progress bar.", | |
# 让帮助信息中的换行符生效 | |
formatter_class=argparse.RawTextHelpFormatter | |
) | |
parser.add_argument( | |
"url", | |
metavar="URL", | |
type=str, | |
help="The URL of the file to download." | |
) | |
parser.add_argument( | |
"-o", "--output", | |
metavar="FILENAME", | |
type=str, | |
default=None, | |
help="Optional. The output filename.\nIf not provided, a timestamp-based name will be generated.\n(e.g., 2025-06-19T10-30-00.mp3)" | |
) | |
args = parser.parse_args() | |
download_file(args.url, args.output) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment