2025年5月29日木曜日

Pythonで複数のファイルをサーバーに自動転送するプログラム

 Pythonで複数のファイルをサーバーに自動転送するプログラムですね。ファイルの転送方法にはいくつか選択肢がありますが、HTMLやCSSファイルといったコンテンツを転送する場合、一般的には以下のモジュールがよく使われます。

  1. FTP (File Transfer Protocol): ftplib モジュールを使用します。最も一般的なファイル転送プロトコルの一つで、比較的簡単に扱えます。
  2. SFTP (SSH File Transfer Protocol): paramiko ライブラリ(またはpysftp)を使用します。SSH経由でファイルを転送するため、セキュリティが高く、より推奨されます。

それぞれの方法について、具体的なコード例を交えて解説します。


前提条件

  • 転送先のサーバーがFTPまたはSFTP接続に対応していること。
  • FTP/SFTPのユーザー名とパスワードが必要になります。
  • SFTPを使用する場合、サーバーのSSHホスト鍵の確認(paramikoの場合)や、もしパスワード認証でなく鍵認証を使う場合は鍵ファイルのパスが必要になります。

1. FTP (ftplib) を使用してファイルを転送する

FTPは比較的シンプルですが、データが暗号化されないため、セキュリティが低いという欠点があります。公開ウェブサイトのファイル転送など、セキュリティ要件がそれほど高くない場合に利用されることがあります。

インストール: ftplibはPythonの標準ライブラリなので、インストールは不要です。

Python
import ftplib
import os

def upload_files_ftp(host, username, password, local_directory, remote_directory):
    """
    指定されたローカルディレクトリから複数のファイルをFTPサーバーにアップロードします。

    Args:
        host (str): FTPサーバーのホスト名またはIPアドレス
        username (str): FTPユーザー名
        password (str): FTPパスワード
        local_directory (str): アップロードするファイルがあるローカルディレクトリのパス
        remote_directory (str): サーバー上のアップロード先のディレクトリのパス
    """
    ftp = ftplib.FTP(host)
    try:
        ftp.login(username, password)
        print(f"FTPサーバー '{host}' にログインしました。")

        # リモートディレクトリに移動または作成
        try:
            ftp.cwd(remote_directory)
            print(f"リモートディレクトリ '{remote_directory}' に移動しました。")
        except ftplib.error_perm:
            print(f"リモートディレクトリ '{remote_directory}' が存在しません。作成を試みます。")
            ftp.mkd(remote_directory)
            ftp.cwd(remote_directory)
            print(f"リモートディレクトリ '{remote_directory}' を作成し、移動しました。")

        # ローカルディレクトリ内のファイルを走査してアップロード
        for filename in os.listdir(local_directory):
            local_filepath = os.path.join(local_directory, filename)
            
            # ディレクトリはスキップ
            if os.path.isdir(local_filepath):
                print(f"'{filename}' はディレクトリなのでスキップします。")
                continue

            # ファイルのアップロード
            try:
                with open(local_filepath, 'rb') as f:
                    ftp.storbinary(f'STOR {filename}', f)
                print(f"ファイル '{filename}' をアップロードしました。")
            except Exception as e:
                print(f"ファイル '{filename}' のアップロード中にエラーが発生しました: {e}")

    except ftplib.all_errors as e:
        print(f"FTP接続または操作中にエラーが発生しました: {e}")
    finally:
        ftp.quit()
        print("FTP接続を閉じました。")

if __name__ == "__main__":
    # 設定情報 (ご自身の情報に置き換えてください)
    FTP_HOST = "your_ftp_server.com"
    FTP_USERNAME = "your_ftp_username"
    FTP_PASSWORD = "your_ftp_password"
    
    # ローカルのアップロード元ディレクトリ
    # 例: このスクリプトと同じディレクトリに 'html_files' というフォルダがあり、その中にHTML/CSSファイルがある場合
    LOCAL_UPLOAD_DIR = "html_files" 
    
    # サーバー上のアップロード先ディレクトリ
    # 例: /public_html/your_website/
    REMOTE_UPLOAD_DIR = "/remote/path/to/your/website" 

    # サンプルファイルを作成 (テスト用)
    if not os.path.exists(LOCAL_UPLOAD_DIR):
        os.makedirs(LOCAL_UPLOAD_DIR)
    with open(os.path.join(LOCAL_UPLOAD_DIR, "index.html"), "w") as f:
        f.write("<!DOCTYPE html><html><head><title>Test Page</title><link rel='stylesheet' href='style.css'></head><body><h1>Hello FTP!</h1></body></html>")
    with open(os.path.join(LOCAL_UPLOAD_DIR, "style.css"), "w") as f:
        f.write("body { font-family: sans-serif; background-color: lightblue; }")
    with open(os.path.join(LOCAL_UPLOAD_DIR, "script.js"), "w") as f:
        f.write("console.log('Hello from JS!');")

    upload_files_ftp(FTP_HOST, FTP_USERNAME, FTP_PASSWORD, LOCAL_UPLOAD_DIR, REMOTE_UPLOAD_DIR)

    # テストファイルを削除 (オプション)
    # os.remove(os.path.join(LOCAL_UPLOAD_DIR, "index.html"))
    # os.remove(os.path.join(LOCAL_UPLOAD_DIR, "style.css"))
    # os.remove(os.path.join(LOCAL_UPLOAD_DIR, "script.js"))
    # os.rmdir(LOCAL_UPLOAD_DIR)

2. SFTP (paramiko) を使用してファイルを転送する

SFTPはSSHプロトコル上で動作するため、データは暗号化され、安全にファイルを転送できます。サーバーにSSH接続が可能であれば、SFTPの使用を強く推奨します。

インストール: paramikoは標準ライブラリではないため、インストールが必要です。

Bash
pip install paramiko

コード例:

Python
import paramiko
import os

def upload_files_sftp(host, port, username, password, local_directory, remote_directory, private_key_path=None):
    """
    指定されたローカルディレクトリから複数のファイルをSFTPサーバーにアップロードします。

    Args:
        host (str): SFTPサーバーのホスト名またはIPアドレス
        port (int): SFTPサーバーのポート番号 (通常は22)
        username (str): SFTPユーザー名
        password (str, optional): SFTPパスワード (鍵認証の場合はNone)
        local_directory (str): アップロードするファイルがあるローカルディレクトリのパス
        remote_directory (str): サーバー上のアップロード先のディレクトリのパス
        private_key_path (str, optional): SSH秘密鍵ファイルのパス (パスワード認証の場合はNone)
    """
    transport = paramiko.Transport((host, port))
    
    try:
        if private_key_path:
            # 鍵認証
            private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
            transport.connect(username=username, pkey=private_key)
            print(f"SFTPサーバー '{host}' に鍵認証で接続しました。")
        else:
            # パスワード認証
            transport.connect(username=username, password=password)
            print(f"SFTPサーバー '{host}' にパスワード認証で接続しました。")

        sftp = paramiko.SFTPClient.from_transport(transport)
        
        # リモートディレクトリに移動または作成
        try:
            sftp.chdir(remote_directory)
            print(f"リモートディレクトリ '{remote_directory}' に移動しました。")
        except FileNotFoundError:
            print(f"リモートディレクトリ '{remote_directory}' が存在しません。作成を試みます。")
            sftp.mkdir(remote_directory)
            sftp.chdir(remote_directory)
            print(f"リモートディレクトリ '{remote_directory}' を作成し、移動しました。")

        # ローカルディレクトリ内のファイルを走査してアップロード
        for filename in os.listdir(local_directory):
            local_filepath = os.path.join(local_directory, filename)
            
            # ディレクトリはスキップ
            if os.path.isdir(local_filepath):
                print(f"'{filename}' はディレクトリなのでスキップします。")
                continue

            # ファイルのアップロード
            try:
                sftp.put(local_filepath, filename) # put(ローカルパス, リモートのファイル名)
                print(f"ファイル '{filename}' をアップロードしました。")
            except Exception as e:
                print(f"ファイル '{filename}' のアップロード中にエラーが発生しました: {e}")

    except paramiko.AuthenticationException:
        print("認証に失敗しました。ユーザー名、パスワード、または鍵ファイルを確認してください。")
    except paramiko.SSHException as e:
        print(f"SSH接続またはSFTP操作中にエラーが発生しました: {e}")
    except Exception as e:
        print(f"予期せぬエラーが発生しました: {e}")
    finally:
        if 'sftp' in locals():
            sftp.close()
        transport.close()
        print("SFTP接続を閉じました。")

if __name__ == "__main__":
    # 設定情報 (ご自身の情報に置き換えてください)
    SFTP_HOST = "your_sftp_server.com"
    SFTP_PORT = 22 # 通常は22
    SFTP_USERNAME = "your_sftp_username"
    SFTP_PASSWORD = "your_sftp_password" # パスワード認証の場合
    SFTP_PRIVATE_KEY_PATH = None # '/path/to/your/private_key.pem' # 鍵認証の場合 (Noneにするとパスワード認証)
    
    LOCAL_UPLOAD_DIR = "html_files" 
    REMOTE_UPLOAD_DIR = "/remote/path/to/your/website" 

    # サンプルファイルを作成 (テスト用)
    if not os.path.exists(LOCAL_UPLOAD_DIR):
        os.makedirs(LOCAL_UPLOAD_DIR)
    with open(os.path.join(LOCAL_UPLOAD_DIR, "index.html"), "w") as f:
        f.write("<!DOCTYPE html><html><head><title>Test Page</title><link rel='stylesheet' href='style.css'></head></head><body><h1>Hello SFTP!</h1></body></html>")
    with open(os.path.join(LOCAL_UPLOAD_DIR, "style.css"), "w") as f:
        f.write("body { font-family: sans-serif; background-color: lightgreen; }")
    with open(os.path.join(LOCAL_UPLOAD_DIR, "another.html"), "w") as f:
        f.write("<!DOCTYPE html><html><body><p>Another test page.</p></body></html>")
    
    upload_files_sftp(SFTP_HOST, SFTP_PORT, SFTP_USERNAME, SFTP_PASSWORD, LOCAL_UPLOAD_DIR, REMOTE_UPLOAD_DIR, SFTP_PRIVATE_KEY_PATH)

    # テストファイルを削除 (オプション)
    # os.remove(os.path.join(LOCAL_UPLOAD_DIR, "index.html"))
    # os.remove(os.path.join(LOCAL_UPLOAD_DIR, "style.css"))
    # os.remove(os.path.join(LOCAL_UPLOAD_DIR, "another.html"))
    # os.rmdir(LOCAL_UPLOAD_DIR)

どちらのモジュールを使うべきか?

  • セキュリティを最優先するなら、SFTP (paramiko) を強く推奨します。 特に本番環境のウェブサイトや機密性の高いファイルを扱う場合は、暗号化されていないFTPは避けるべきです。
  • サーバーがSFTPに対応していない、または非常に古いシステムでFTPしか利用できない場合は、ftplibを使用することになります。
  • paramikoはSSHの機能が豊富なので、SFTPだけでなくSSHコマンドの実行なども可能です。

注意事項

  • 認証情報: スクリプト内に直接ユーザー名やパスワードをハードコードするのは、セキュリティ上好ましくありません。本番環境では、環境変数、設定ファイル、または安全な設定管理ツール(例: python-dotenv, configparser)などから読み込むことを検討してください。
  • エラーハンドリング: 上記のコードでは基本的なエラーハンドリングを行っていますが、実際の運用ではより詳細なエラーログの記録やリトライ処理などを実装することが重要です。
  • ディレクトリの同期: 上記のスクリプトは指定したローカルディレクトリ内のファイルをリモートにアップロードするだけです。サブディレクトリの再帰的なアップロード、ファイルが既に存在する場合の挙動(上書き、スキップなど)、ローカルで削除されたファイルの同期などは考慮されていません。これらの高度な機能が必要な場合は、Rsyncのような専用ツールを呼び出すか、より複雑なロジックを実装する必要があります。
  • SSHホスト鍵の確認 (paramiko): paramikoを使用する際、デフォルトでは既知のホスト(~/.ssh/known_hosts)に登録されていないサーバーへの接続は警告が出たり、接続が拒否されたりすることがあります。厳密なセキュリティを求める場合は、client.set_missing_host_key_policy(paramiko.RejectPolicy()) を使う前に、ホスト鍵を事前に確認・登録する必要があります。開発段階では paramiko.AutoAddPolicy() を使うこともありますが、本番環境では非推奨です。

ご自身のサーバーの環境とセキュリティ要件に合わせて、最適な方法を選択してください。SFTPが利用可能であれば、そちらを優先することをお勧めします。

0 件のコメント:

コメントを投稿