文章・議事録・ログ・メモの中に散らばったURLだけを、まとめて抜き出して一覧にしたい。
そんな時に効くのが「URL抽出 → 一覧化」。さらに重複を消せると、リンク集づくりや確認作業が一気に楽になる。
この記事では、秀丸マクロから PowerShell を呼び出して、
- 選択範囲(なければ全文)からURLを抽出
- 抽出結果を 新規タブ に一覧出力
- 重複排除(保持順 or ソート)も選べる
…を一発でやる。
できること
http:///https:///www.のURLを抽出- 末尾につきがちな
。)],などの“邪魔記号”をある程度取り除く(実用寄り) - 出力モード
- 重複排除(出現順を保持)
- 重複排除(ソートして集合化)
- 重複そのまま
- 抽出結果は 新規タブに出る(元の文章は壊さない)
導入手順(3分)
1) ファイル配置(この形にする)
マクロフォルダ(秀丸のマクロ用フォルダ)に、次の構成で置く。
(マクロフォルダ)
├─ url_extract_list.mac
└─ filters
└─ extract_urls.ps1
2) マクロ登録
秀丸で url_extract_list.mac を開いて、必要なら「マクロ登録」。
(キー割り当てしておくと快適)
PowerShell:URL抽出フィルタ(filters\extract_urls.ps1)
param(
# 出力モード
# - unique_keep: 重複排除(出現順を保持)
# - unique_sort: 重複排除(ソートして集合化)
# - all_keep : 重複排除なし(全部出す)
[ValidateSet("unique_keep", "unique_sort", "all_keep")]
[string]$Mode = "unique_keep",
# www.example.com みたいなURLに https:// を付けて正規化する
[switch]$AddScheme,
# 重複判定を大小無視でやる(用途次第。URLは大小区別があり得るので注意)
[switch]$CaseInsensitive
)
# UTF-8で入出力(秀丸側 runex の encode=6 と合わせる)
try {
$utf8 = New-Object System.Text.UTF8Encoding($false)
[Console]::InputEncoding = $utf8
[Console]::OutputEncoding = $utf8
} catch {}
$text = [Console]::In.ReadToEnd()
if ([string]::IsNullOrEmpty($text)) { exit 0 }
# URLっぽいものを拾う(実用寄り)
# - http/https
# - www.
$pattern = '(?i)\bhttps?://[^\s<>"''`]+|\bwww\.[^\s<>"''`]+'
# 末尾に付きがちな“邪魔記号”を落とす(完全パーサではない。現場向け)
function Trim-TrailingPunct([string]$u) {
if ([string]::IsNullOrEmpty($u)) { return $u }
$trimChars = @(
'.', ',', ';', ':', '!', '?',
'"', "'", '”', '’', '»',
'。', '、', '!', '?',
'>', '〉', '》', '」', '』', '】',
'…',
' ' # 全角スペース
)
while ($u.Length -gt 0) {
$last = $u.Substring($u.Length - 1, 1)
# 単純に落とす系
if ($trimChars -contains $last) {
$u = $u.Substring(0, $u.Length - 1)
continue
}
# ) ] } は「囲みURL」にくっつきやすいので、括弧バランスを見て落とす
if ($last -eq ")") {
$open = ([regex]::Matches($u, '\(')).Count
$close = ([regex]::Matches($u, '\)')).Count
if ($close -gt $open) {
$u = $u.Substring(0, $u.Length - 1)
continue
}
}
if ($last -eq "]") {
$open = ([regex]::Matches($u, '\[')).Count
$close = ([regex]::Matches($u, '\]')).Count
if ($close -gt $open) {
$u = $u.Substring(0, $u.Length - 1)
continue
}
}
if ($last -eq "}") {
$open = ([regex]::Matches($u, '\{')).Count
$close = ([regex]::Matches($u, '\}')).Count
if ($close -gt $open) {
$u = $u.Substring(0, $u.Length - 1)
continue
}
}
break
}
return $u
}
$matches = [regex]::Matches($text, $pattern)
if ($matches.Count -eq 0) { exit 0 }
$urls = New-Object System.Collections.Generic.List[string]
foreach ($m in $matches) {
$u = $m.Value
# <https://...> のような囲みを軽く剥がす
if ($u.StartsWith("<")) { $u = $u.TrimStart("<") }
if ($u.EndsWith(">")) { $u = $u.TrimEnd(">") }
$u = Trim-TrailingPunct $u
if ($AddScheme -and $u -match '^(?i)www\.') {
$u = "https://$u"
}
if (-not [string]::IsNullOrWhiteSpace($u)) {
$urls.Add($u)
}
}
switch ($Mode) {
"all_keep" {
$urls | ForEach-Object { $_ }
}
"unique_sort" {
if ($CaseInsensitive) {
$set = New-Object System.Collections.Generic.HashSet[string]([System.StringComparer]::OrdinalIgnoreCase)
} else {
$set = New-Object System.Collections.Generic.HashSet[string]
}
foreach ($u in $urls) { [void]$set.Add($u) }
$arr = $set.ToArray()
[Array]::Sort($arr, [System.StringComparer]::Ordinal)
$arr | ForEach-Object { $_ }
}
default { # unique_keep
if ($CaseInsensitive) {
$seen = New-Object System.Collections.Generic.HashSet[string]([System.StringComparer]::OrdinalIgnoreCase)
} else {
$seen = New-Object System.Collections.Generic.HashSet[string]
}
foreach ($u in $urls) {
if ($seen.Add($u)) { $u }
}
}
}
秀丸マクロ:抽出して新規タブに一覧出力(url_extract_list.mac)
/*
url_extract_list.mac
選択(なければ全文)→ PowerShellでURL抽出 → 新規タブに一覧出力
元の文章は変更しない。
*/
#scope = val(input("対象: 1=選択(無ければ全文) / 2=全文", "1"));
#mode = val(input(
"出力モード: 1=重複排除(順保持) / 2=重複排除(ソート) / 3=重複そのまま",
"1"
));
#add = val(input("www. に https:// を付ける: 0=しない / 1=する", "0"));
#ci = val(input("重複判定を大小無視: 0=しない / 1=する", "0"));
if (#scope == 2 || !selecting) {
selectall;
}
$ps1 = macrodir + "\\filters\\extract_urls.ps1";
if (!existfile($ps1)) {
message "見つからない: " + $ps1 + "\r\nfilters\\extract_urls.ps1 を配置しろ。";
endmacro;
}
if (#mode == 2) {
$modeArg = "unique_sort";
} else if (#mode == 3) {
$modeArg = "all_keep";
} else {
$modeArg = "unique_keep";
}
$addArg = "";
if (#add == 1) $addArg = " -AddScheme";
$ciArg = "";
if (#ci == 1) $ciArg = " -CaseInsensitive";
$cmd = "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"" + $ps1 + "\" -Mode " + $modeArg + $addArg + $ciArg;
# runex:選択をstdinに渡して、stdoutは「新規ファイル(新規タブ)」へ
# - stdin : 5=選択範囲
# - stdout : 4=新規ファイル
# - encode : 6=UTF-8
runex $cmd
, 1
, 5, ""
, 4, ""
, 7, ""
, 1, ""
, 0
, 1
, 6
, 1
;
if (!result) {
message "失敗。PowerShell実行、パス、ExecutionPolicy を確認。";
endmacro;
}
message "URL抽出 完了(新規タブに一覧を出した)";
使い方
1) URLが含まれる文章を開く
2) 必要なら範囲選択(選択しないなら全文対象)
3) マクロ実行
4) 抽出URLが 新規タブ にズラッと並ぶ
出力モードのおすすめ
- 重複排除(順保持):リンクが出てきた順番で並ぶ。確認用途に強い
- 重複排除(ソート):集合として整理される。差分比較や棚卸しに強い
- 重複そのまま:ログの“頻出リンク”を数えたい前処理に向く(後で集計する用)
ハマりやすいポイント
URLの末尾に「。」や「)」が付く
実用上よくあるので、末尾の邪魔記号はある程度落とすようにしてある。
ただし“完全に正しいURLパーサ”ではない。データに合わせて Trim-TrailingPunct を増やせばOK。
hxxp:// みたいに崩されている
それは抽出しない(このフィルタの対象外)。
必要なら、別で hxxp→http を戻してから抽出する。
www. だけだと扱いづらい
マクロ実行時に「wwwに https:// を付ける」を 1 にすれば、https://www... に正規化して出す。
おまけ:もっと強くする改造ネタ
utm_など追跡パラメータを削ってから重複排除(リンク集が一気に綺麗になる)- ドメイン別にグルーピングして見出しを付ける(整理が楽)
- 抽出結果に連番やコメントを付けて、レビュー用テンプレにする
まとめ
文章の中からURLだけ抜き出して一覧化できると、確認・引用・整理が速くなる。
秀丸は外部コマンド連携が強いので、こういう“抽出系”を一度作るとずっと使える。
