多執行緒下載nginx站點目錄下檔案

&UnstopPable發表於2024-09-18

程式碼如下

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庫來實現多執行緒併發下載,從而提高下載速度。指令碼的主要功能包括:

  1. 下載單個檔案:使用requests模組獲取檔案,配合tqdm顯示進度條。
  2. 獲取目錄連結:透過解析HTML頁面,提取當前目錄下的所有檔案和子目錄。
  3. 併發下載:如果有子目錄,則建立執行緒池併發處理子目錄和檔案。
  4. 遞迴下載:對於每個子目錄,遞迴下載其中的檔案。

相關文章