🐥
PowerShell:ファイルの一括ダウンロードとリネーム
とあるサイトからファイルを一括でダウンロードして、ファイルのリネームをしたかったので作ってみた。
概要
大量のファイルがアップされているサイトで、リンクテキストとダウンロードされるファイル名が異なるので、リンクテキストにリネームするもの。
処理の流れ
- ダウンロードするサイトを指定
- リンクを取得
- ファイルを一個ずつダウンロード
- ファイルをリンクテキストにリネーム
完成したスクリプト
FileDL_RN.ps1
<#
.SYNOPSIS
指定したWebサイトから特定の拡張子ファイル(PDF, XLSX等)を一括ダウンロードし、
リンクのテキスト(または属性値)をファイル名として安全にリネームして保存する。
#>
#-----------------------------------------------------------------------------
# 【設定項目】ダウンロード対象の拡張子を指定(カンマ区切りで追加・削除可能)
$TargetExtensions = @("pdf", "xlsx", "zip", "docx")
#-----------------------------------------------------------------------------
# 【重要:プロキシ環境およびSSL/TLSエラー回避のネットワーク設定】
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
# URL形式のチェック用Function
function IsAbsoluteUrl([string] $url){
return [System.Uri]::IsWellFormedUriString($url, [System.UriKind]::Absolute)
}
# 堅牢化された安全なファイル名生成Function
Function Get-SafeFileName {
param([String]$Name)
if ([string]::IsNullOrWhiteSpace($Name)) { return "NoTitle" }
$invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
$re = "[{0}]" -f [RegEx]::Escape($invalidChars)
$cleanName = $Name -replace $re, ''
$cleanName = $cleanName -replace "[\r\n\t]", ''
$cleanName = $cleanName.Trim().TrimEnd('.')
if ([string]::IsNullOrWhiteSpace($cleanName)) { return "InvalidName" }
if ($cleanName -match "^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$") { $cleanName = "_" + $cleanName }
if ($cleanName.Length -gt 100) { $cleanName = $cleanName.Substring(0, 100) }
return $cleanName
}
#-----------------------------------------------------------------------------
# メイン処理開始
#-----------------------------------------------------------------------------
# 動的に正規表現パターンを生成
$regexPattern = "\.(" + ($TargetExtensions -join "|") + ")$"
Write-Host "抽出対象の拡張子パターン: $regexPattern" -ForegroundColor DarkGray
$TargetUrl = Read-Host "(ファイルを取得したいサイトのURLを入力してください)"
if (-not (IsAbsoluteUrl($TargetUrl))) {
Write-Error "$TargetUrl は正しい形式のURLではありません。"
exit
}
$uri = New-Object System.Uri ($TargetUrl)
# フォルダ選択ダイアログ表示
Add-Type -AssemblyName System.Windows.Forms
$FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{
RootFolder = "Desktop"
Description = "ファイルを保存するフォルダを選択してください"
}
if ($FolderBrowser.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) {
Write-Warning "フォルダ選択がキャンセルされました。"
exit
}
$SavePath = $FolderBrowser.SelectedPath
try {
Write-Host "サイト情報を取得中..." -ForegroundColor Cyan
$response = Invoke-WebRequest -Uri $uri -UseBasicParsing
# 文字コードの動的判定
$charset = "UTF-8"
if ($response.Headers["Content-Type"] -match "charset=([^\s;]+)") {
$charset = $matches[1]
} elseif ($response.Content -match 'charset\s*=\s*["'']?([a-zA-Z0-9\-_]+)') {
$charset = $matches[1]
}
# リンクの抽出
$links = $response.Links | Where-Object { $_.href -match $regexPattern -and -not [string]::IsNullOrWhiteSpace($_.href) }
if ($null -eq $links -or $links.Count -eq 0) {
Write-Warning "指定された拡張子($($TargetExtensions -join ', '))のファイルが見つかりません。"
exit
}
Write-Host "合計 $($links.Count) 件の対象ファイルを発見。ダウンロードを開始します。"
foreach ($link in $links) {
$OriginalFileName = Split-Path $link.href -Leaf
if (IsAbsoluteUrl($link.href)) {
$DownloadUrl = New-Object System.Uri ($link.href)
} else {
$DownloadUrl = New-Object System.Uri ($uri, $link.href)
}
# ---------------------------------------------------------
# スマート・エンコーディング処理(破壊的変換の防止)
# ---------------------------------------------------------
if ($response.Headers["Content-Type"] -match "charset=") {
$a_Tag = $link.outerHTML
} else {
$bytes = [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetBytes($link.outerHTML)
try {
$a_Tag = [System.Text.Encoding]::GetEncoding($charset).GetString($bytes)
} catch {
$a_Tag = [System.Text.Encoding]::GetEncoding('UTF-8').GetString($bytes)
}
}
# ---------------------------------------------------------
# ファイル名抽出ロジック
# ---------------------------------------------------------
$rawText = ""
# 優先順位1: テキスト情報を最優先
$innerText = $a_Tag -replace "<[^>]+>", " "
$innerText = [System.Net.WebUtility]::HtmlDecode($innerText)
$innerText = $innerText -replace "[\r\n\t]", " "
$innerText = $innerText -replace "\s+", " "
$innerText = $innerText.Trim()
if (-not [string]::IsNullOrWhiteSpace($innerText)) {
$rawText = $innerText
} else {
# 優先順位2: テキストが空の場合のみ属性値(aria-label, title, alt)を探す
if ($a_Tag -match 'aria-label\s*=\s*(["''])(.*?)\1') {
$rawText = $matches[2]
} elseif ($a_Tag -match 'title\s*=\s*(["''])(.*?)\1') {
$rawText = $matches[2]
} elseif ($a_Tag -match '<img[^>]+alt\s*=\s*(["''])(.*?)\1') {
$rawText = $matches[2]
}
}
# 最終的な無害化
$SafeBaseName = Get-SafeFileName -Name $rawText
$Extension = [System.IO.Path]::GetExtension($OriginalFileName)
# 保存先パスの決定(同名ファイル回避)
$NewFileName = "${SafeBaseName}${Extension}"
$OutFilePath = Join-Path $SavePath $NewFileName
$counter = 1
while (Test-Path -Path $OutFilePath) {
$NewFileName = "${SafeBaseName}_${counter}${Extension}"
$OutFilePath = Join-Path $SavePath $NewFileName
$counter++
}
# ダウンロード実行
Write-Host "DL中: $NewFileName" -NoNewline
Invoke-WebRequest -Uri $DownloadUrl.AbsoluteUri -OutFile $OutFilePath
Write-Host " [完了]" -ForegroundColor Green
}
Write-Host "すべての処理が完了しました。" -ForegroundColor Cyan
} catch {
Write-Error "処理中にエラーが発生しました。`n詳細: $($_.Exception.Message)"
}
参考
とても参考にさせていただきました。
Discussion