はじめに:ファイル自動化の必要性

パソコンを使っていると、ファイルやフォルダを扱う作業にかなりの時間を費やすことになります。数百のファイル名を変更したり、特定条件のファイルを探して整理したり、バックアップのためにファイルをコピーするなどの作業は、手作業で行うと時間がかかり、ミスも起きやすいです。

Pythonはファイルシステムを扱う強力なツールを提供しています。今回は、Pythonのos、pathlib、shutil、globモジュールを活用して、ファイルとフォルダを効率的に自動化する方法を学びます。

1. osモジュールの基礎

osモジュールは、オペレーティングシステムと対話するためのPythonの基本モジュールです。ファイルシステム作業に必須の機能を提供します。

1.1 基本的なパス操作

import os

# 現在の作業ディレクトリを確認
current_dir = os.getcwd()
print(f"現在のディレクトリ:{current_dir}")

# 作業ディレクトリを変更
os.chdir("/path/to/directory")

# 環境変数にアクセス
home_dir = os.environ.get('HOME')  # Linux/Mac
user_profile = os.environ.get('USERPROFILE')  # Windows

# ユーザーホームディレクトリ(クロスプラットフォーム)
home = os.path.expanduser("~")
print(f"ホームディレクトリ:{home}")

1.2 パスの操作

import os

# パスの結合(OSに適した区切り文字を使用)
full_path = os.path.join("folder", "subfolder", "file.txt")
print(full_path)  # Windows: folder\subfolder\file.txt

# パスの分離
directory = os.path.dirname("/path/to/file.txt")  # /path/to
filename = os.path.basename("/path/to/file.txt")  # file.txt

# ファイル名と拡張子の分離
name, extension = os.path.splitext("document.pdf")
print(f"ファイル名:{name}、拡張子:{extension}")  # document, .pdf

# 絶対パスを取得
abs_path = os.path.abspath("relative/path/file.txt")

# パスの存在確認
if os.path.exists("/path/to/check"):
    print("パスが存在します。")

# ファイルかフォルダかを確認
if os.path.isfile("/path/to/file.txt"):
    print("ファイルです。")

if os.path.isdir("/path/to/folder"):
    print("フォルダです。")

1.3 ディレクトリの一覧取得

import os

# ディレクトリ内の項目一覧
items = os.listdir("/path/to/directory")
print(items)  # ['file1.txt', 'folder1', 'file2.py']

# ファイルのみをフィルタリング
files = [f for f in os.listdir(".") if os.path.isfile(f)]

# フォルダのみをフィルタリング
folders = [f for f in os.listdir(".") if os.path.isdir(f)]

# 特定の拡張子のファイルのみをフィルタリング
python_files = [f for f in os.listdir(".") if f.endswith(".py")]

1.4 ディレクトリの走査(os.walk)

import os

# すべてのサブディレクトリを走査
for root, dirs, files in os.walk("/path/to/start"):
    print(f"現在のディレクトリ:{root}")
    print(f"サブフォルダ:{dirs}")
    print(f"ファイル:{files}")
    print("-" * 50)

# 特定の拡張子のファイルをすべて検索
def find_files_by_extension(start_path, extension):
    """指定されたパスから特定の拡張子のファイルをすべて検索します。"""
    found_files = []
    for root, dirs, files in os.walk(start_path):
        for file in files:
            if file.endswith(extension):
                full_path = os.path.join(root, file)
                found_files.append(full_path)
    return found_files

# すべての.txtファイルを検索
txt_files = find_files_by_extension(".", ".txt")
for f in txt_files:
    print(f)

2. pathlibモジュールの活用

pathlibはPython 3.4で導入されたオブジェクト指向のファイルシステムパスライブラリです。os.pathよりも直感的でモダンな方法でパスを扱うことができます。

2.1 Pathオブジェクトの基本

from pathlib import Path

# Pathオブジェクトを作成
p = Path("/path/to/file.txt")
current = Path(".")
home = Path.home()

# パスの結合(/演算子を使用)
full_path = Path.home() / "Documents" / "project" / "file.txt"
print(full_path)

# パスの属性
print(p.name)       # file.txt(ファイル名)
print(p.stem)       # file(拡張子を除いたファイル名)
print(p.suffix)     # .txt(拡張子)
print(p.parent)     # /path/to(親ディレクトリ)
print(p.parts)      # ('/', 'path', 'to', 'file.txt')

# 絶対パス
abs_path = Path("relative/path").resolve()

# 存在確認
if p.exists():
    print("存在します。")

if p.is_file():
    print("ファイルです。")

if p.is_dir():
    print("ディレクトリです。")

2.2 ディレクトリの探索

from pathlib import Path

# 現在のディレクトリのすべての項目
for item in Path(".").iterdir():
    print(item)

# 特定のパターンにマッチ(現在のディレクトリのみ)
for py_file in Path(".").glob("*.py"):
    print(py_file)

# 再帰的なパターンマッチ(すべてのサブディレクトリ)
for py_file in Path(".").rglob("*.py"):
    print(py_file)

# 複数の拡張子を検索
extensions = ["*.jpg", "*.png", "*.gif"]
images = []
for ext in extensions:
    images.extend(Path(".").rglob(ext))

# ファイルのみをフィルタリング
files_only = [p for p in Path(".").iterdir() if p.is_file()]

# フォルダのみをフィルタリング
dirs_only = [p for p in Path(".").iterdir() if p.is_dir()]

2.3 ファイル/フォルダの作成と削除

from pathlib import Path

# ディレクトリを作成
new_dir = Path("new_folder")
new_dir.mkdir(exist_ok=True)  # すでに存在してもエラーなし

# ネストされたディレクトリを作成
nested_dir = Path("parent/child/grandchild")
nested_dir.mkdir(parents=True, exist_ok=True)

# ファイルを作成(空ファイル)
new_file = Path("new_file.txt")
new_file.touch()

# ファイルを削除
if new_file.exists():
    new_file.unlink()

# 空のディレクトリを削除
if new_dir.exists() and new_dir.is_dir():
    new_dir.rmdir()  # ディレクトリが空である必要があります

# ファイル名を変更
old_path = Path("old_name.txt")
new_path = Path("new_name.txt")
if old_path.exists():
    old_path.rename(new_path)

3. ファイルの読み書き

Pythonでファイルを読み書きする方法を学びます。with文を使用するとファイルが自動的に閉じられるため、推奨されます。

3.1 テキストファイルの処理

# ファイルに書き込み
with open("example.txt", "w", encoding="utf-8") as f:
    f.write("1行目\n")
    f.write("2行目\n")

# ファイルに内容を追加
with open("example.txt", "a", encoding="utf-8") as f:
    f.write("追加された行\n")

# ファイルを読み込み(全体)
with open("example.txt", "r", encoding="utf-8") as f:
    content = f.read()
    print(content)

# ファイルを読み込み(行単位)
with open("example.txt", "r", encoding="utf-8") as f:
    for line in f:
        print(line.strip())  # 改行文字を削除

# ファイルを読み込み(リストとして)
with open("example.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()
    print(lines)  # ['1行目\n', '2行目\n', ...]

# 複数行を一度に書き込み
lines_to_write = ["行1\n", "行2\n", "行3\n"]
with open("output.txt", "w", encoding="utf-8") as f:
    f.writelines(lines_to_write)

3.2 pathlibでファイルの読み書き

from pathlib import Path

# 簡単なファイル書き込み
Path("simple.txt").write_text("Hello, World!", encoding="utf-8")

# 簡単なファイル読み込み
content = Path("simple.txt").read_text(encoding="utf-8")
print(content)

# バイナリファイルの読み込み
binary_content = Path("image.png").read_bytes()

# バイナリファイルの書き込み
Path("copy.png").write_bytes(binary_content)

3.3 CSVファイルの処理

import csv

# CSVファイルへの書き込み
data = [
    ["名前", "年齢", "都市"],
    ["田中太郎", 30, "東京"],
    ["山田花子", 25, "大阪"],
    ["佐藤次郎", 28, "名古屋"]
]

with open("data.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# CSVファイルの読み込み
with open("data.csv", "r", encoding="utf-8-sig") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# 辞書でCSVを扱う
with open("data.csv", "r", encoding="utf-8-sig") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"{row['名前']}は{row['年齢']}歳です。")

3.4 JSONファイルの処理

import json

# JSONファイルへの書き込み
data = {
    "name": "田中太郎",
    "age": 30,
    "skills": ["Python", "JavaScript", "SQL"],
    "address": {
        "city": "東京",
        "district": "渋谷区"
    }
}

with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# JSONファイルの読み込み
with open("data.json", "r", encoding="utf-8") as f:
    loaded_data = json.load(f)
    print(loaded_data["name"])
    print(loaded_data["skills"])

4. フォルダの作成/削除/移動

4.1 フォルダの作成

import os
from pathlib import Path

# osでフォルダを作成
os.makedirs("parent/child/grandchild", exist_ok=True)

# pathlibでフォルダを作成
Path("another/nested/folder").mkdir(parents=True, exist_ok=True)

# 日付別フォルダを作成
from datetime import datetime

today = datetime.now().strftime("%Y-%m-%d")
daily_folder = Path("backups") / today
daily_folder.mkdir(parents=True, exist_ok=True)

4.2 フォルダの削除

import os
import shutil
from pathlib import Path

# 空のフォルダを削除
os.rmdir("empty_folder")
# または
Path("empty_folder").rmdir()

# 内容のあるフォルダを削除(注意:復元不可!)
shutil.rmtree("folder_with_contents")

# 安全な削除(確認後に削除)
def safe_delete_folder(folder_path):
    """フォルダを安全に削除します。"""
    path = Path(folder_path)
    if not path.exists():
        print(f"'{folder_path}'が存在しません。")
        return False

    # フォルダの内容を確認
    items = list(path.rglob("*"))
    file_count = sum(1 for item in items if item.is_file())
    folder_count = sum(1 for item in items if item.is_dir())

    print(f"削除するフォルダ:{folder_path}")
    print(f"含まれるファイル:{file_count}個")
    print(f"含まれるフォルダ:{folder_count}個")

    confirm = input("本当に削除しますか?(yes/no):")
    if confirm.lower() == "yes":
        shutil.rmtree(path)
        print("削除完了!")
        return True
    else:
        print("キャンセルされました。")
        return False

5. ファイル検索(glob)

globモジュールは、Unixシェルスタイルのパターンマッチングを使用してファイルを検索します。

5.1 globの基本的な使い方

import glob

# 現在のディレクトリのすべての.txtファイル
txt_files = glob.glob("*.txt")

# 特定のフォルダのすべての.pyファイル
py_files = glob.glob("src/*.py")

# 再帰的検索(すべてのサブフォルダ)
all_py_files = glob.glob("**/*.py", recursive=True)

# 複数の拡張子を検索
import itertools
extensions = ["*.jpg", "*.png", "*.gif"]
images = list(itertools.chain.from_iterable(
    glob.glob(ext, recursive=True) for ext in ["**/" + e for e in extensions]
))

# パターンマッチングの例
# ? : 単一の文字
# * : すべての文字(0個以上)
# [abc] : a, b, cのいずれか
# [0-9] : 数字

files = glob.glob("file?.txt")      # file1.txt, fileA.txt
files = glob.glob("data[0-9].csv")  # data0.csv ~ data9.csv
files = glob.glob("[!_]*.py")       # アンダースコアで始まらない.py

5.2 pathlibのglob

from pathlib import Path

# 現在のディレクトリで検索
for txt_file in Path(".").glob("*.txt"):
    print(txt_file)

# 再帰的検索
for py_file in Path(".").rglob("*.py"):
    print(py_file)

# 複雑なパターン
for file in Path("data").glob("**/report_*.xlsx"):
    print(file)

6. ファイルのコピー/移動(shutil)

shutilモジュールは、ファイルとフォルダの高レベル操作(コピー、移動、削除)のための機能を提供します。

6.1 ファイルのコピー

import shutil

# ファイルをコピー(メタデータなし)
shutil.copy("source.txt", "destination.txt")

# ファイルをコピー(メタデータ含む)
shutil.copy2("source.txt", "destination.txt")

# フォルダにコピー(元のファイル名を維持)
shutil.copy("source.txt", "backup_folder/")

# フォルダ全体をコピー
shutil.copytree("source_folder", "destination_folder")

# 特定のファイルを除いてコピー
def ignore_patterns(directory, files):
    """特定のパターンのファイルを無視します。"""
    return [f for f in files if f.endswith('.pyc') or f.startswith('.')]

shutil.copytree("source", "dest", ignore=ignore_patterns)

# または組み込み関数を使用
shutil.copytree("source", "dest",
                ignore=shutil.ignore_patterns('*.pyc', '*.tmp', '__pycache__'))

6.2 ファイルの移動

import shutil

# ファイルを移動
shutil.move("source.txt", "new_location/source.txt")

# ファイル名を変更(同じフォルダ内で移動)
shutil.move("old_name.txt", "new_name.txt")

# フォルダを移動
shutil.move("source_folder", "new_location/")

# 安全な移動関数
from pathlib import Path

def safe_move(source, destination):
    """ファイルを安全に移動します。"""
    src = Path(source)
    dst = Path(destination)

    if not src.exists():
        print(f"ソース'{source}'が存在しません。")
        return False

    # 移動先がフォルダならソースファイル名を維持
    if dst.is_dir():
        dst = dst / src.name

    # 移動先がすでに存在する場合は確認
    if dst.exists():
        confirm = input(f"'{dst}'はすでに存在します。上書きしますか?(y/n):")
        if confirm.lower() != 'y':
            print("キャンセルされました。")
            return False

    shutil.move(str(src), str(dst))
    print(f"'{source}' -> '{dst}' 移動完了")
    return True

7. ファイル名の一括変更

ファイル名を一括で変更することは、自動化の代表的な活用例です。

7.1 基本的なファイル名変更

import os
from pathlib import Path

# プレフィックスを追加
def add_prefix(folder, prefix):
    """フォルダ内のすべてのファイルにプレフィックスを追加します。"""
    folder_path = Path(folder)
    for file in folder_path.iterdir():
        if file.is_file():
            new_name = folder_path / f"{prefix}{file.name}"
            file.rename(new_name)
            print(f"'{file.name}' -> '{new_name.name}'")

# サフィックスを追加
def add_suffix(folder, suffix):
    """ファイル名にサフィックスを追加します(拡張子の前)。"""
    folder_path = Path(folder)
    for file in folder_path.iterdir():
        if file.is_file():
            new_name = folder_path / f"{file.stem}{suffix}{file.suffix}"
            file.rename(new_name)
            print(f"'{file.name}' -> '{new_name.name}'")

7.2 連番でファイル名を変更

from pathlib import Path

def rename_with_sequence(folder, base_name, start=1, padding=3):
    """ファイル名を連番に変更します。

    例:image_001.jpg, image_002.jpg, ...
    """
    folder_path = Path(folder)
    files = sorted([f for f in folder_path.iterdir() if f.is_file()])

    for i, file in enumerate(files, start=start):
        # 連番をフォーマット(001, 002, ...)
        sequence = str(i).zfill(padding)
        new_name = folder_path / f"{base_name}_{sequence}{file.suffix}"

        file.rename(new_name)
        print(f"'{file.name}' -> '{new_name.name}'")

7.3 日付ベースのファイル名変更

from pathlib import Path
from datetime import datetime
import os

def rename_with_date(folder, include_time=False):
    """ファイルの更新日付に基づいて名前を変更します。"""
    folder_path = Path(folder)

    for file in folder_path.iterdir():
        if file.is_file():
            # ファイルの更新時刻を取得
            mtime = os.path.getmtime(file)
            date_obj = datetime.fromtimestamp(mtime)

            if include_time:
                date_str = date_obj.strftime("%Y%m%d_%H%M%S")
            else:
                date_str = date_obj.strftime("%Y%m%d")

            new_name = folder_path / f"{date_str}_{file.name}"

            # 重複を防止
            counter = 1
            while new_name.exists():
                new_name = folder_path / f"{date_str}_{counter}_{file.name}"
                counter += 1

            file.rename(new_name)
            print(f"'{file.name}' -> '{new_name.name}'")

7.4 正規表現でファイル名を変更

import re
from pathlib import Path

def rename_with_regex(folder, pattern, replacement):
    """正規表現を使用してファイル名を変更します。"""
    folder_path = Path(folder)

    for file in folder_path.iterdir():
        if file.is_file():
            new_name = re.sub(pattern, replacement, file.stem)
            if new_name != file.stem:
                new_path = folder_path / f"{new_name}{file.suffix}"
                file.rename(new_path)
                print(f"'{file.name}' -> '{new_path.name}'")

# 使用例
# スペースをアンダースコアに
rename_with_regex(".", r"\s+", "_")

# 特殊文字を削除
rename_with_regex(".", r"[^\w\-_.]", "")

8. 重複ファイルの検出

ハードディスクの容量を節約するために、重複ファイルを見つけて整理するスクリプトを作成してみましょう。

import hashlib
from pathlib import Path
from collections import defaultdict

def get_file_hash(filepath, chunk_size=8192):
    """ファイルのMD5ハッシュ値を計算します。"""
    hasher = hashlib.md5()
    with open(filepath, 'rb') as f:
        while chunk := f.read(chunk_size):
            hasher.update(chunk)
    return hasher.hexdigest()

def find_duplicates(folder):
    """フォルダ内の重複ファイルを検出します。"""
    folder_path = Path(folder)

    # ステップ1:ファイルサイズでグループ化
    size_dict = defaultdict(list)
    for file in folder_path.rglob("*"):
        if file.is_file():
            size_dict[file.stat().st_size].append(file)

    # ステップ2:同じサイズのファイルのみハッシュを比較
    hash_dict = defaultdict(list)
    for size, files in size_dict.items():
        if len(files) > 1:  # 同じサイズのファイルが2つ以上の場合のみ
            for file in files:
                file_hash = get_file_hash(file)
                hash_dict[file_hash].append(file)

    # ステップ3:重複ファイルをフィルタリング
    duplicates = {h: files for h, files in hash_dict.items() if len(files) > 1}

    return duplicates

def report_duplicates(folder):
    """重複ファイルのレポートを出力します。"""
    duplicates = find_duplicates(folder)

    if not duplicates:
        print("重複ファイルはありません。")
        return

    total_wasted = 0
    print("=" * 60)
    print("重複ファイルレポート")
    print("=" * 60)

    for hash_value, files in duplicates.items():
        file_size = files[0].stat().st_size
        wasted = file_size * (len(files) - 1)
        total_wasted += wasted

        print(f"\nハッシュ:{hash_value[:16]}...")
        print(f"ファイルサイズ:{file_size:,} bytes")
        print(f"重複数:{len(files)}個")
        print("ファイル一覧:")
        for f in files:
            print(f"  - {f}")

    print("\n" + "=" * 60)
    print(f"合計無駄な容量:{total_wasted:,} bytes ({total_wasted / 1024 / 1024:.2f} MB)")
    print("=" * 60)

def delete_duplicates(folder, keep='first'):
    """重複ファイルを削除します。

    Args:
        folder: 対象フォルダ
        keep: 'first'(最初を保持)または 'newest'(最新ファイルを保持)
    """
    duplicates = find_duplicates(folder)

    deleted_count = 0
    freed_space = 0

    for hash_value, files in duplicates.items():
        if keep == 'newest':
            # 更新日時でソート(最新が先)
            files = sorted(files, key=lambda f: f.stat().st_mtime, reverse=True)

        # 最初のファイルを保持、残りを削除
        for file in files[1:]:
            file_size = file.stat().st_size
            print(f"削除:{file}")
            file.unlink()
            deleted_count += 1
            freed_space += file_size

    print(f"\n削除されたファイル:{deleted_count}個")
    print(f"確保された容量:{freed_space:,} bytes ({freed_space / 1024 / 1024:.2f} MB)")

9. ファイル整理自動化プロジェクト

これまで学んだ内容を総合して、実用的なファイル整理自動化プロジェクトを作成してみましょう。

"""
高度なファイル整理自動化スクリプト
- 拡張子別分類
- 日付別分類
- サイズ別分類
- 重複ファイル処理
- ロギングサポート
"""

import os
import shutil
import hashlib
import logging
from pathlib import Path
from datetime import datetime
from collections import defaultdict

# ロギング設定
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('file_organizer.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# 拡張子別カテゴリマッピング
CATEGORIES = {
    'Images': ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg', '.webp', '.ico'],
    'Documents': ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt', '.rtf', '.odt'],
    'Videos': ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm'],
    'Music': ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma', '.m4a'],
    'Archives': ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'],
    'Programs': ['.exe', '.msi', '.dmg', '.deb', '.rpm', '.apk'],
    'Code': ['.py', '.js', '.html', '.css', '.java', '.cpp', '.c', '.h', '.json', '.xml'],
    'Data': ['.csv', '.sql', '.db', '.sqlite']
}

def get_category(extension):
    """拡張子に対応するカテゴリを返します。"""
    ext_lower = extension.lower()
    for category, extensions in CATEGORIES.items():
        if ext_lower in extensions:
            return category
    return 'Others'

def get_file_hash(filepath):
    """ファイルのMD5ハッシュを計算します。"""
    hasher = hashlib.md5()
    with open(filepath, 'rb') as f:
        for chunk in iter(lambda: f.read(8192), b''):
            hasher.update(chunk)
    return hasher.hexdigest()

class FileOrganizer:
    def __init__(self, source_folder, dest_folder=None):
        self.source = Path(source_folder)
        self.dest = Path(dest_folder) if dest_folder else self.source
        self.stats = {
            'moved': 0,
            'skipped': 0,
            'duplicates': 0,
            'errors': 0
        }
        self.hash_dict = defaultdict(list)

    def organize_by_extension(self):
        """ファイルを拡張子別に整理します。"""
        logger.info(f"拡張子別整理開始:{self.source}")

        for file in self.source.iterdir():
            if file.is_file():
                category = get_category(file.suffix)
                target_folder = self.dest / category
                target_folder.mkdir(exist_ok=True)

                target_path = target_folder / file.name

                # 重複ファイル名の処理
                if target_path.exists():
                    target_path = self._get_unique_path(target_path)

                try:
                    shutil.move(str(file), str(target_path))
                    logger.info(f"移動:{file.name} -> {category}/")
                    self.stats['moved'] += 1
                except Exception as e:
                    logger.error(f"エラー:{file.name} - {e}")
                    self.stats['errors'] += 1

        self._print_stats()

    def organize_by_date(self, date_format="%Y-%m"):
        """ファイルを更新日付別に整理します。"""
        logger.info(f"日付別整理開始:{self.source}")

        for file in self.source.iterdir():
            if file.is_file():
                mtime = datetime.fromtimestamp(file.stat().st_mtime)
                date_folder = mtime.strftime(date_format)

                target_folder = self.dest / date_folder
                target_folder.mkdir(exist_ok=True)

                target_path = target_folder / file.name

                if target_path.exists():
                    target_path = self._get_unique_path(target_path)

                try:
                    shutil.move(str(file), str(target_path))
                    logger.info(f"移動:{file.name} -> {date_folder}/")
                    self.stats['moved'] += 1
                except Exception as e:
                    logger.error(f"エラー:{file.name} - {e}")
                    self.stats['errors'] += 1

        self._print_stats()

    def _get_unique_path(self, path):
        """重複しないファイルパスを返します。"""
        counter = 1
        new_path = path
        while new_path.exists():
            new_path = path.parent / f"{path.stem}_{counter}{path.suffix}"
            counter += 1
        return new_path

    def _print_stats(self):
        """統計を出力します。"""
        logger.info("=" * 50)
        logger.info("処理結果:")
        logger.info(f"  移動されたファイル:{self.stats['moved']}個")
        logger.info(f"  スキップされたファイル:{self.stats['skipped']}個")
        logger.info(f"  重複ファイル:{self.stats['duplicates']}個")
        logger.info(f"  エラー:{self.stats['errors']}個")
        logger.info("=" * 50)


def main():
    """メイン実行関数"""
    print("=" * 60)
    print("高度なファイル整理自動化")
    print("=" * 60)

    source = input("整理するフォルダパス:").strip()
    if not source:
        source = str(Path.home() / "Downloads")
        print(f"デフォルトパスを使用:{source}")

    print("\n整理方式を選択してください:")
    print("1. 拡張子別整理")
    print("2. 日付別整理")

    choice = input("\n選択 (1-2):").strip()

    organizer = FileOrganizer(source)

    if choice == '1':
        organizer.organize_by_extension()
    elif choice == '2':
        organizer.organize_by_date()
    else:
        print("無効な選択です。")


if __name__ == "__main__":
    main()

10. まとめと次回予告

今回はPythonを活用したファイル&フォルダ自動化の核心技術を学びました:

  • osモジュールを活用した基本的なファイルシステム操作
  • pathlibのオブジェクト指向パス処理
  • ファイルの読み書き(テキスト、CSV、JSON)
  • フォルダの作成、削除、移動
  • globを活用したファイル検索
  • shutilを活用したファイルのコピー/移動
  • ファイル名の一括変更技法
  • 重複ファイルの検出と処理
  • 総合ファイル整理自動化プロジェクト

この知識は、今後のさまざまな自動化プロジェクトの基礎となります。ファイルシステムを自在に扱えれば、バックアップ自動化、ログ分析、データ前処理など、無限の活用が可能です。

次回予告:Python自動化マスター第3編では、Webスクレイピング自動化を扱います。requests、BeautifulSoup、Seleniumを活用してWebからデータを自動収集する方法を学びます!