fetch_images.rb - Markdown画像取得・更新ツール by #Claude
結城浩: 以下は、Claude Codeが作成した文書とコードです。
概要
fetch_images.rb
は、Markdownファイル内の外部画像URLを検出し、ローカルにダウンロードして、Markdownファイル内のURLをローカルパスに自動更新するRubyスクリプトです。
主な機能
- Markdownファイル内の画像URL(

形式とHTMLの<img>
タグ)を検出 - 外部画像をローカルディレクトリにダウンロード
- Markdownファイル内の画像URLをローカルパスに自動更新
- 元のMarkdownファイルのバックアップを作成
- ソースファイルごとに整理された画像ディレクトリ構造
使用方法
基本的な使い方
ruby fetch_images.rb [ディレクトリ] [出力ディレクトリ] [オプション]
パラメータ
ディレクトリ
(省略可能): Markdownファイルを検索するディレクトリ。デフォルトは現在のディレクトリ(.
)出力ディレクトリ
(省略可能): 画像を保存するディレクトリ。デフォルトはfetched_images
--no-update
(オプション): このフラグを指定すると、画像のダウンロードのみ行い、Markdownファイルは更新しません
使用例
# カレントディレクトリのMarkdownファイルを処理
ruby fetch_images.rb
# 特定のディレクトリを指定
ruby fetch_images.rb ./documents
# 出力ディレクトリを指定
ruby fetch_images.rb . ./images
# Markdownファイルを更新せずに画像のみダウンロード
ruby fetch_images.rb . ./images --no-update
動作の詳細
1. ディレクトリ構造
画像は以下のような構造で保存されます:
fetched_images/
├── ドキュメント名1/
│ ├── image_1.jpg
│ ├── image_2.png
│ └── ...
├── ドキュメント名2/
│ ├── image_1.jpg
│ └── ...
└── ...
2. 画像の命名規則
- ダウンロードされた画像は
image_1.jpg
、image_2.png
のように連番で命名されます - 拡張子は元のファイルから取得し、不明な場合は
.jpg
がデフォルトで使用されます
3. 対応する画像形式
- Markdown形式:

- HTML形式:
<img src="画像URL" ...>
- HTTP/HTTPSのURLのみ対応(データURIはスキップ)
4. バックアップ
- Markdownファイルを更新する前に、
.backup
拡張子を付けたバックアップファイルが作成されます - 例:
document.md
→document.md.backup
必要な環境
- Ruby(標準ライブラリのみ使用)
- インターネット接続(外部画像のダウンロード用)
エラー処理
- ダウンロードに失敗した画像URLは元のまま保持されます
- エラーメッセージが表示されますが、処理は続行されます
- データURIや非HTTP/HTTPSのURLは自動的にスキップされます
注意事項
- 大量の画像を含むMarkdownファイルを処理する場合、ダウンロードに時間がかかることがあります
- 同じURLの画像が複数回出現する場合、一度だけダウンロードされ、すべての参照が同じローカルパスに更新されます
- バックアップファイルは自動的に削除されないため、必要に応じて手動で削除してください
ライセンス
このスクリプトは個人使用を目的として作成されています。使用は自己責任でお願いします。
コード
#!/usr/bin/env ruby
require 'open-uri'
require 'fileutils'
require 'uri'
require 'net/http'
class MarkdownImageFetcher
def initialize(directory = '.', output_dir = 'fetched_images', update_markdown = true)
@directory = directory
@output_dir = output_dir
@update_markdown = update_markdown
@image_mappings = {}
end
def run
create_output_directory
scan_and_process_markdown_files
puts "\nCompleted!"
end
private
def create_output_directory
FileUtils.mkdir_p(@output_dir)
puts "Created output directory: #{@output_dir}"
end
def scan_and_process_markdown_files
markdown_files = Dir.glob(File.join(@directory, '*.md'))
if markdown_files.empty?
puts "No Markdown files found in #{@directory}"
return
end
puts "Found #{markdown_files.count} Markdown files"
markdown_files.each do |file|
process_markdown_file(file)
end
end
def process_markdown_file(file)
content = File.read(file)
filename = File.basename(file)
base_name = filename.gsub('.md', '')
puts "\nProcessing: #{filename}"
# Track image mappings for this file
file_mappings = {}
image_counter = 1
# Process Markdown image syntax: 
modified_content = content.gsub(/!\[([^\]]*)\]\(([^)]+)\)/) do |match|
alt_text = $1
url = $2
new_path = download_and_get_new_path(url, base_name, image_counter)
if new_path
file_mappings[url] = new_path
image_counter += 1
""
else
match # Keep original if download failed
end
end
# Process HTML img tags: <img src="url" ...>
modified_content = modified_content.gsub(/<img([^>]+)src=["']([^"']+)["']([^>]*)>/i) do |match|
prefix = $1
url = $2
suffix = $3
# Check if we already processed this URL
if file_mappings[url]
new_path = file_mappings[url]
else
new_path = download_and_get_new_path(url, base_name, image_counter)
if new_path
file_mappings[url] = new_path
image_counter += 1
end
end
if new_path
"<img#{prefix}src=\"#{new_path}\"#{suffix}>"
else
match # Keep original if download failed
end
end
# Save the modified content if requested
if @update_markdown && file_mappings.any?
backup_file = file + '.backup'
File.write(backup_file, content)
File.write(file, modified_content)
puts " Updated #{filename} (backup saved as #{File.basename(backup_file)})"
puts " Replaced #{file_mappings.count} image URLs"
end
end
def download_and_get_new_path(url, base_name, index)
# Skip if URL is a data URI
if url.start_with?('data:')
puts " Skipping data URI"
return nil
end
begin
# Parse URL
uri = URI.parse(url)
# Determine filename and extension
original_filename = File.basename(uri.path)
extension = File.extname(original_filename)
extension = '.jpg' if extension.empty? # Default extension
# Create new filename
new_filename = "image_#{index}#{extension}"
# Create subdirectory for this markdown file
source_dir = File.join(@output_dir, base_name)
FileUtils.mkdir_p(source_dir)
# Full output path
output_path = File.join(source_dir, new_filename)
# Download the image
print " Downloading #{url}..."
if uri.scheme =~ /^https?$/
URI.open(url) do |remote_file|
File.open(output_path, 'wb') do |local_file|
local_file.write(remote_file.read)
end
end
puts " Done!"
# Return the relative path that will be used in the markdown
return "#{base_name}/#{new_filename}"
else
puts " Skipped (not HTTP/HTTPS)"
return nil
end
rescue => e
puts " Error: #{e.message}"
return nil
end
end
end
# Main execution
if __FILE__ == $0
# Parse command line arguments
directory = ARGV[0] || '.'
output_dir = ARGV[1] || 'fetched_images'
update_markdown = !ARGV.include?('--no-update')
puts "Markdown Image Fetcher and Updater"
puts "=================================="
puts "Scanning directory: #{directory}"
puts "Output directory: #{output_dir}"
puts "Update Markdown files: #{update_markdown}"
puts "(Use --no-update flag to only download without modifying markdown files)"
puts
fetcher = MarkdownImageFetcher.new(directory, output_dir, update_markdown)
fetcher.run
end
(2025年6月4日)