🐥
PowerShell:ファイルの一括ダウンロードとリネーム
とあるサイトからファイルを一括でダウンロードして、ファイルのリネームをしたかったので作ってみた。
概要
大量のファイルがアップされているサイトで、リンクテキストとダウンロードされるファイル名が異なるので、リンクテキストにリネームするもの。
処理の流れ
- ダウンロードするサイトを指定
- リンクを取得(今回はPDF,XLSX,ZIPに絞った)
- ファイルを一個ずつダウンロード
- ファイルをリンクテキストにリネーム
ハマったポイント
[Invoke-WebRequest]のLinksでリンクテキストを取得しようとしたが、文字化けしてしまう。
[Invoke-WebRequest]は、ISO-8859-1にエンコードされてしまうので、サイトの文字コードを判断したうえで、処理が必要だった。
サイトの文字コード判断も綺麗ではないがサイトのhtmlをダウンロードし、そこからMETAタグ内のcharsetがUTF-8か検索してやってた。
もっと綺麗なやり方があれば教えてください。。。
完成したスクリプト
FileDL_RN.ps1
#-----------------------------------------------------------------------------
# URL形式のチェック用Function作成
function IsAbsoluteUrl([string] $url){
return [System.Uri]::IsWellFormedUriString($url, [System.UriKind]::Absolute);
}
#-----------------------------------------------------------------------------
# 禁止文字の削除用Function作成
Function Remove-InvalidFileNameChars {
param(
[Parameter(Mandatory=$true,
Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String]$Name
)
$invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
$re = "[{0}]" -f [RegEx]::Escape($invalidChars)
return ($Name -replace $re)
}
#-----------------------------------------------------------------------------
# 一括ダウンロード&リネーム処理開始
# URLの指定
$TargetUrl = Read-Host "(ファイルを取得したいサイトのURLを入力してください。)"
if (-not (IsAbsoluteUrl($TargetUrl))) {
Write-Output "$TargetUrl は正しい形式のURLではありません。"
exit;
}
$uri = New-Object System.Uri ($TargetUrl)
# SSL/TLSのエラーが発生する場合、以下のコメントアウトを解除して、再度実行してみてください。
# [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# フォルダ選択ダイアログ表示
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-Output "フォルダ選択がキャンセルされました。"
exit;
}
# 選択したフォルダパスの格納
$SavePath = $FolderBrowser.SelectedPath
# 文字コード判定(2022/09/02追記)
# sourceファイルの取得
Invoke-WebRequest -Uri $uri -UseBasicParsing -OutFile "$SavePath\tmp.txt"
# METAタグ内のcharsetがUTF-8か検索
$Encoding_Flag = Select-String "meta.*charset.*UTF-8" "$SavePath\tmp.txt" -Quiet
# tmpファイル削除
Remove-Item "$SavePath\tmp.txt"
try{
# Webページから、ファイルへのリンクを取得
# 指定されたuriを取得
$response = Invoke-WebRequest -Uri $uri -UseBasicParsing
# aタグ(outerHTML)とリンク(href)を取得
# お好みで拡張子を変更
# PDFの場合
# $links = $response.Links | Where-Object { $_.href -like "*.pdf" } | Select-Object outerHTML, href
# PDF,XLSXの場合
# $links = $response.Links | Where-Object { $_.href -like "*.pdf" -or $_.href -like "*.xlsx" } | Select-Object outerHTML, href
# PDF,XLSX,ZIPの場合(2022/09/01追記)
$links = $response.Links | Where-Object { $_.href -like "*.pdf" -or $_.href -like "*.xlsx" -or $_.href -like "*.zip" } | Select-Object outerHTML, href
if ($links.Count -eq 0) {
Write-Output ダウンロード対象のファイルがありません。
return;
}
# 個々のファイルダウンロード
foreach ($link in $links) {
# ダウンロード用のファイル名抽出
$FileName = Split-Path $link.href -Leaf
# 保存先パス作成(フォルダ + ファイル名)
$OutFilePath = Join-Path $SavePath $FileName
if (IsAbsoluteUrl($link.href)) {
# 絶対パスの場合:そのまま利用する
$DownloadUrl = New-Object System.Uri ($link.href)
} else {
# DL対象ファイルのURL取得(Uriの機能で、絶対パスと相対パスをくっつける)
$DownloadUrl = New-Object System.Uri ($uri, $link.href)
}
# ファイルのダウンロード
Invoke-WebRequest -Uri $DownloadUrl.AbsoluteUri -OutFile $OutFilePath
# ファイル名の変更
# byte列変換_文字化け対策(2022/09/02追記)
$bytes = [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetBytes($link.outerHTML)
# バイト型→文字コードを指定して変換(UTF-8とShift_JISのみ対応)
if ($Encoding_Flag){
# UTF-8
$a_Tag = [System.Text.Encoding]::GetEncoding('UTF-8').GetString($bytes)
} else {
# "Shift_JIS"
$a_Tag = [System.Text.Encoding]::GetEncoding('Shift_JIS').GetString($bytes)
}
# aタグからファイル名を抽出
$NewFileText = $a_Tag -replace (".*<a.*?>(.*?)<(.|\n)*", '$1')
if ($NewFileText -ne ""){
# 禁止・不要文字削除
$ReName = Remove-InvalidFileNameChars "$NewFileText" | ForEach-Object {$_.replace(" ", "")}
# 置換前のファイルの拡張子を取得
$Str_Extension = [System.IO.Path]::GetExtension($FileName)
# リネーム
Rename-Item $OutFilePath -NewName "$ReName$Str_Extension"
}
}
}catch [System.Net.WebException]{
$statusCode = $_.Exception.Response.StatusCode;
Write-Output エラー発生のため、処理を中断します。
Write-Output "エラーステータス: $statusCode"
}
参考
とても参考にさせていただきました。
Discussion