PowerShell 实现批量下载文件

时间:2021-01-04 09:54:22

简介

批量文件下载器 PowerShell 版,类似于迅雷批量下载功能,且可以破解 Referer 防盗链

 

源代码

git: https://gitee.com/codefelix/spany-down-ps.git

PowerShell 实现批量下载文件PowerShell 实现批量下载文件
## ===============================================================================
Write-Output "---------------------------------------------"
Write-Output "批量文件下载器 PowerShell版"
Write-Output "@product  spany-down-ps"
Write-Output "@author   felix"
Write-Output "@date     2019-04-19"
Write-Output "---------------------------------------------"
Write-Output ""
## ===============================================================================

# 接收是否输入
function ReadInput_YesOrNo([string]$message, [switch]$defaultValue) {
    while($true) {
        $input = Read-Host $message
        if(![String]::IsNullOrWhiteSpace($input)) {
            if($input -eq 'y' -or $input -eq 'n') {
                return $input -eq 'y'
            }
        }
        if($defaultValue) {
            return $args -eq $true
        }
    }
}

# 接收文本输入
function ReadInput_Text([string]$message, [string]$defaultValue) {
    while($true) {
        $input = Read-Host $message
        if(![String]::IsNullOrWhiteSpace($input)) {
            return $input.Trim()
        }
        if($defaultValue) {
            return $defaultValue
        }
    }
}

# 接收整数输入
function ReadInput_Integer([string]$message, [int]$minValue=[Int32]::MinValue, [int]$maxValue=[Int32]::MaxValue) {
    $defaultValue = $message
    while($true) {
        $input = Read-Host $message
        if([String]::IsNullOrWhiteSpace($input)) {
            $message = $defaultValue
            continue
        }

        $input = $input.Trim()
        [int]$result = 0
        if(![Int32]::TryParse($input, [ref]$result)) {
            $message = "必须输入一个整数"
            continue
        }
        elseif($result -lt $minValue -or $result -gt $maxValue) {
            $message = "整数范围必须是 $minValue-$maxValue,请重新输入"
            continue
        }

        return $result
    }
}

# 接收URL输入
function ReadInput_Url([string]$message, [string]$defaultValue) {
    $defaultMessage = $message
    while($true) {
        $input = Read-Host $message
        if([String]::IsNullOrWhiteSpace($input)) {
            if($defaultValue){
                return $defaultValue
            }
            $message = $defaultMessage
            continue
        }
        
        $input = $input.Trim()
        if($input.Length -lt 18 -or !($input -clike 'http://*' -or $input -clike 'https://*')) {
            $message = "URL 格式不正确,请重新输入"
            continue
        }

        return $input
    }
}

# 接收目录输入
function ReadInput_Path([string]$message, [string]$defaultValue, [bool]$createIfNotExist = $true) {
    $path = ""
    while($true) {
        $input = Read-Host $message
        if(![String]::IsNullOrWhiteSpace($input)) {
            $path = $input.Trim()
        }
        elseif($defaultValue) {
            $path = $defaultValue
        }
        else {
            continue
        }
        
        if($createIfNotExist -and ![System.IO.Directory]::Exists($path)) {    # 或者 Test-Path -Path $path 但无法识别路径中的括号
            New-Item -Path $path -ItemType Directory | Out-Null
        }

        return $path
    }
}

# 构建URL列表
function BuildUrlList([string]$urlFormat, [int]$start, [int]$end, [int]$len) {    
    if(!$urlFormat.Contains("(*)")) {
        return ,$urlFormat
    }
    $list = @()
    $last = $end + 1
    for($i = $start; $i -lt $last; $i++) {    
        $tmp_url = $urlFormat -replace "\(\*\)",("{0:D$len}" -f $i) ## 或者 $i.ToString("D$len")
        $list += $tmp_url
    }
    return $list
}

[int]$script:completed = 0  # 下载完成数量
[int]$script:succeed = 0    # 下载成功数量

# 开始下载(普通方法)
function StartDownload([array]$urlList, [string]$path, [string]$referer) {    
    $last = $urlList.Count
    $watch = Measure-Command {
        for($i = 0; $i -lt $last; $i++) {
            DownloadItem -url $urlList[$i] -path $path -referer $referer
            Start-Sleep -Milliseconds 200  # 延迟0.2秒
        }
    }
    $failed = $script:completed - $succeed
    $elapsed = [Math]::Round($watch.TotalMilliseconds/1000, 2)  # 总计耗时(秒)
    Write-Output ""
    Write-Host "总共下载 $script:completed,成功 $script:succeed,失败 $failed,耗时 $elapsed s" -ForegroundColor Red -BackgroundColor Yellow
    $script:completed = 0
    $script:succeed = 0
}

# 下载单个文件
function DownloadItem([string]$url, [string]$path, [string]$referer) {
    $url_file = $url.Substring($url.LastIndexOf('/') + 1);
    if($referer.Contains("(*)")) {
        $referer = $referer -replace "\(\*\)", $url
    }
    try{
        $tmpFileName = [System.IO.Path]::GetTempFileName()
        $destFileName = [System.IO.Path]::Combine($path, $url_file)
        $watch = Measure-Command {
            # 下载文件到临时文件夹
            Invoke-WebRequest -Uri $url -Method Get -Headers @{"Referer"=$referer} -UserAgent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36" -TimeoutSec 120 -OutFile $tmpFileName
            # 将临时文件移动到目标文件夹
            Move-Item -Path $tmpFileName -Destination $destFileName -Force
        }
        $script:succeed += 1
        $fileLength =[Math]::Ceiling((Get-Item -LiteralPath $destFileName).Length / 1024.0)
        $elapsed = [Math]::Round($watch.TotalMilliseconds)
        # 下载成功!12.jpg - 115KB/2356ms
        Write-Host "下载成功!$url_file - $fileLength KB/$elapsed ms" -ForegroundColor Green
    } catch {
        Write-Error $PSItem.ToString()
    } finally {
        $script:completed += 1
    }
}

# 主函数 运行 AppStart 即可启动
function AppStart {
    $urlFormat = ReadInput_Url -message "输入URL(含通配符,例如 http://www.spany.com/2019/(*).jpg)"
    $start = ReadInput_Integer -message "通配符数字开始(0~200)" -minValue 0 -maxValue 200
    $end = $start + 200
    $end = ReadInput_Integer -message "通配符数字结束($start~$end)" -minValue: $start -maxValue $end
    $len = ReadInput_Integer -message "通配符数字长度(1~5)" -minValue: 1 -maxValue 5
    $referer = ReadInput_Url -message "输入Referer为破解防盗链(如果Referer中含有通配符(*),则将被当前URL替换,如无须Referer则直接回车)" -defaultValue "https://www.baidu.com/visit"
    Write-Output ""

    $urlList = BuildUrlList -urlFormat $urlFormat -start $start -end $end -len $len
    if($urlList.Count -gt 0) {
        Write-Output "URL列表如下:"
        foreach($url in $urlList) {
            Write-Output "`t$url"
        }
        Write-Output ""
        if(ReadInput_YesOrNo -message "是否开始下载?(y/n)") {
            $path = ReadInput_Path -message "输入文件存储目录"
            Write-Output ""
            StartDownload -urlList $urlList -path $path -referer $referer
        }
    } else {
        Write-Warning "不能创建URL列表,请核对参数!"
    }
    Write-Output ""
}


AppStart
spany-down-ps.ps1

 

如何使用

以管理员方式打开 PowerShell 命令终端,定位到文件目录,运行 ./spany-down-ps.ps1 即可,如提示此系统上禁止运行脚本,可执行命令 Set-ExecutionPolicy -ExecutionPolicy Unrestricted 更改执行策略

然后按照屏幕提示,输入必要参数,启动下载进程,截图演示下载美图录的美女写真集(宅男福利啊!)

如果你直接打开图片或者用迅雷批量下载,都将被 403 Forbidden,因为网站启用了 Referer 防盗链

PowerShell 实现批量下载文件

不过 PowerShell 版还是单线程顺序下载,如果不嫌麻烦,想提高下载效率,可以用另一个 C#/.NET Core 版 https://gitee.com/codefelix/spany-down-sharp