程式碼如下
import os
import time
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm
base_url = "" # 要下載檔案的基礎URL
download_dir = "" # 儲存下載檔案的目錄
max_workers = 10 # 最大併發數
# 下載單個檔案
def download_file(file_url, download_dir):
if not os.path.exists(download_dir):
os.makedirs(download_dir)
file_name = file_url.split('/')[-1] # 提取檔名
file_path = os.path.join(download_dir, file_name)
if os.path.isdir(file_path):
print(f"錯誤:路徑 {file_path} 是一個目錄,無法作為檔案儲存!")
return
# 如果檔案不存在則開始下載
if not os.path.exists(file_path):
with requests.get(file_url, stream=True) as r:
r.raise_for_status() # 確保請求成功
total_size = int(r.headers.get('content-length', 0)) # 獲取檔案大小
with open(file_path, 'wb') as f, tqdm(
desc=file_path,
total=total_size,
unit='B',
unit_scale=True,
unit_divisor=1024
) as bar:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
bar.update(len(chunk))
else:
print(f"檔案 {file_path} 已存在,跳過下載。")
# 獲取目錄中的所有檔案和子目錄連結
def get_links(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
files = [] # 存放檔案連結
subdirs = [] # 存放子目錄連結
for link in soup.find_all('a'):
href = link.get('href')
if href not in ['../', '/']: # 排除上級目錄連結
full_url = urljoin(url, href) # 構建完整URL
if href.endswith('/'):
subdirs.append(full_url) # 是子目錄則新增到子目錄列表
else:
files.append(full_url) # 是檔案則新增到檔案列表
return files, subdirs
# 併發下載檔案和子目錄中的檔案
def download_concurrently(base_url, download_dir):
files, subdirs = get_links(base_url)
if not subdirs: # 如果沒有子目錄,只順序下載檔案
print(f"檢測到目錄下全是檔案,順序下載...")
for file_url in files:
download_file(file_url, download_dir)
else:
print(f"檢測到有子目錄,進行併發下載...")
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = []
for subdir in subdirs:
subdir_name = subdir.split('/')[-2]
subdir_path = os.path.join(download_dir, subdir_name)
# 避免檔案和目錄同名衝突
if os.path.isfile(subdir_path):
print(f"錯誤:路徑 {subdir_path} 已存在且是檔案,無法建立為目錄!")
continue
futures.append(executor.submit(download_files, subdir, subdir_path))
# 順序下載當前目錄下的檔案
for file_url in files:
futures.append(executor.submit(download_file, file_url, download_dir))
# 顯示併發任務的進度
for future in as_completed(futures):
try:
future.result()
except Exception as e:
print(f"下載出錯: {e}")
# 遞迴下載子目錄中的檔案
def download_files(url, download_dir):
files, subdirs = get_links(url)
if not os.path.exists(download_dir):
os.makedirs(download_dir)
for file_url in files:
download_file(file_url, download_dir)
for subdir in subdirs:
subdir_name = subdir.split('/')[-2]
subdir_path = os.path.join(download_dir, subdir_name)
if os.path.isfile(subdir_path):
print(f"錯誤:路徑 {subdir_path} 已存在且是檔案,無法建立為目錄!")
continue
download_files(subdir, subdir_path)
if __name__ == "__main__":
start_time = time.time() # 記錄開始時間
download_concurrently(base_url, download_dir)
end_time = time.time() # 記錄結束時間
total_time = end_time - start_time
print(f"總下載時間: {total_time:.2f} 秒")
說明
指令碼用於從一個目錄中下載檔案,並且支援遞迴處理子目錄。它使用了concurrent.futures
庫來實現多執行緒併發下載,從而提高下載速度。指令碼的主要功能包括:
- 下載單個檔案:使用
requests
模組獲取檔案,配合tqdm
顯示進度條。 - 獲取目錄連結:透過解析HTML頁面,提取當前目錄下的所有檔案和子目錄。
- 併發下載:如果有子目錄,則建立執行緒池併發處理子目錄和檔案。
- 遞迴下載:對於每個子目錄,遞迴下載其中的檔案。