【Python】WPプラグイン BackWpupのバックアップファイルを定期的にローカルに保存するPythonプログラム作成してみた

Python
この記事は約13分で読めます。

この記事では、WordPressのプラグイン、BackWPupのバックアップデータをpythonでローカルに定期的にダウンロードする方法を解説します。

プログラムの流れは、FTPでバックアップファイルがあるサーバーにログインし、バックアップファイルをローカルに保存するというものです。

目次の流れで実装しました。

不明点などあれば、コメントしていただければと思います。

※python version : 3.11.7

設定ファイル作成

以下の設定を書いています。ファイル名はconfig.pyの想定でプログラムを書いています。

  • FPT接続情報
  • FTPサーバーのバックアップファイル保存先
  • ローカルのバックアップファイル保存先
  • ログファイルの保存先
  • バックアップファイルの保存世代数
Python
# ログイン情報
FTP_HOST_NAME = 'your ftp host name'
FTP_USER_ID = 'ftp user id'
FTP_PASSWORD = 'ftp password'

# FTPサーバーのバックアップ保存ディレクトリ
BACKUP_DIR = '/ftp/backupfile/saved/path'

# ローカルの保存先
SAVE_TO = '/local/path/to/save/backupfile'

# ログファイルの設定
LOG_FOLDER = '/local/path/to/save/logfile'
LOG_FILE_NAME = 'your log file name' # bk_download.logとか

# バックアップ保存世代数(例えば3にすると、ローカルに最大3つのバックアップファイルが保存される。)
N_GEN = 3

私は、スターサーバーを使っているので、スターサーバーを例に「FTP接続情報」、「FTPサーバーのバックアップファイル保存先」の確認方法を解説します。他の項目はご自身で設定してください。

ちなみに、スターサーバーは安くておすすめです。

FTP接続情報

まずはFTP_HOST_NAMEです。スターサーバーにログインし、「サーバー管理ツール > サーバー情報」を表示します。そこのホスト名がFTP_HOST_NAMEの値です。

次に、FTP_USER_ID、FTP_USER_PASSWORDです。同じく「サーバー管理ツール」から、「FTPアカウント設定」を表示し、対象のドメインを選択します。

アカウント名がFTP_USER_IDです。そのアカウントに対するパスワードがFTP_USER_PASSWORDです。パスワードは表示されないので、忘れた場合は、FTPアカウントを追加するなどして、パスワードを確認する必要があります。

FTPサーバーのアックアップ保存ディレクトリ(BACKUP_DIR)

wordpressにログインし、BackWPupの「バックアップ」をクリックします。その画面にある「フォルダー」列がバックアップファイルの保存先になっています。例えばこれが「/a/b/c/d/e」とします。

ローカルにダウンロードする際の、FTPアカウント一覧の「編集」メニューからそのアカウントのFTPサーバーのルートパスを確認します。例えばこれが「/a/b/c」とします。

この場合、BACKUP_DIRは「/d/e」となります。つまり、バックアップファイル保存先のパスの、FTPサーバーのルートパス以下がBACKUP_DIRになります。

バックアップファイルダウンロード

以下が先ほどの設定ファイルをもとに、FTPサーバーからバックアップファイルをダウンロードするプログラムです。

Python
import logging
import glob
import os
import ftplib

from config import *


class BackupDownloader:

    def __init__(self, ftp_host_name, ftp_user_id, ftp_passwd, 
            backup_dir, save_to, n_gen, log_folder, log_file_name):
        self.ftp_host_name = ftp_host_name
        self.ftp_user_id = ftp_user_id
        self.ftp_passwd = ftp_passwd
        self.backup_dir = backup_dir
        self.save_to = save_to
        self.n_gen = n_gen
        self._init_logger(log_folder, log_file_name)

    def download(self):
        """
        バックアップファイルをダウンロード
        """
        ftp = None
        try:
            ftp = ftplib.FTP(self.ftp_host_name)
            ftp.login(user=self.ftp_user_id, passwd=self.ftp_passwd)
            ftp.cwd(self.backup_dir)
            for bk_file_name in ftp.nlst('*.zip'):
                # 既に存在する場合はSKIP
                save_path = os.path.join(SAVE_TO, bk_file_name)
                if os.path.exists(save_path):
                    continue
                
                # ない場合だけdownload
                with open(save_path, 'wb') as f:
                    ftp.retrbinary(f'RETR {bk_file_name}', f.write)
                
                self.logger.info(f'file donwload successful: {bk_file_name}')
        except ftplib.all_errors as e:
            import traceback
            self.logger.error("An exception occurred: %s", str(e))
            self.logger.error(traceback.format_exc())
        finally:
            if ftp is not None:
                ftp.quit()

        self._clean()
    
    def _clean(self):
        """
        バックアップファイル数をN_GEN以下にする
        """
        bk_file_path_list = glob.glob(os.path.join(self.save_to, '*.zip'))
        n_del = len(bk_file_path_list) - self.n_gen

        # 削除なし
        if n_del <= 0: 
            return

        # 古いbkファイルを削除
        bk_file_path_list = sorted(bk_file_path_list)
        for f in bk_file_path_list[:n_del]:
            try:
                os.remove(f)
                file_name = os.path.basename(f)
                self.logger.info(f'file delete successful: {file_name}')
            except Exception as e:
                import traceback
                self.logger.error("An exception occurred: %s", str(e))
                self.logger.error(traceback.format_exc())

    def _init_logger(self, log_folder, log_file_name):
        """
        loggerの初期化
        """
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s [%(levelname)s]: %(message)s')
        handler = logging.FileHandler(
                filename=os.path.join(log_folder, log_file_name), encoding='utf-8')
        handler.setFormatter(formatter)
        logger.addHandler(handler)

        self.logger = logger


if __name__ == '__main__':
    bk_downloader = BackupDownloader(
            FTP_HOST_NAME, FTP_USER_ID, FTP_PASSWORD, BACKUP_DIR, SAVE_TO, N_GEN,
            LOG_FOLDER, LOG_FILE_NAME)
    bk_downloader.download()

順番に解説していきます。

6行目で前節で書いた設定内容を読み込んでいます。

21行目のdownloadメソッドの中で、FTPサーバーにログインし、バックアップファイルをダウンロードしています。バックアップファイルの拡張子は「zip」を想定しているので、他の拡張子の場合はそこを書き換える必要があります。(設定ファイルに外だししてもいいですね。)

既にバックアップファイルがローカルに存在する場合はスキップし、ない場合だけローカルにダウンロードしています。49行目で_cleanメソッドを呼び出して、ローカルのバックアップファイル数をN_GEN以下にしています。(設定ファイルで設定した、ローカルのバックアップファイル世代数)

74行目の_init_loggerでは、バックアップファイルダウンロードのログを出力するための設定をしています。このプログラムは毎日定期実行する想定で、何かのエラーが原因で、ダウンロードできなかった時の原因分析のためにロガーの設定をしました。self.logger.~と書かれている箇所が、ログをファイルに出力しているところです。

89行目以下で、バックアップファイルのダウンロードを実行するインスタンスを作成し、実際にダウンロードしています。

定期実行する

先ほどのプログラムを毎日手動で実行するのは面倒くさいので、定期実行します。

私は、linuxでcondaの仮想環境を使っているので、condaの設定、condaの環境activate、先ほどのpythonファイルの実行を行うシェルスクリプトを用意しました。

ShellScript
#!/bin/sh
__conda_setup="$('/home/sr-exp/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/home/sr-exp/anaconda3/etc/profile.d/conda.sh" ]; then
        . "/home/sr-exp/anaconda3/etc/profile.d/conda.sh"
    else
        export PATH="/home/sr-exp/anaconda3/bin:$PATH"
    fi
fi
unset __conda_setup
conda activate Download_BackWPup
python /home/sr-exp/sr-experience/doc/download_BackWPup_to_local/run.py

用意したシェルスクリプトをcronで実行するように設定します。以下のコマンドでcronの設定ファイルを開きます。

crontab -e

開かれたファイルに、いつ、何をするかを書きます。私は毎日4時に、先ほどのシェルスクリプトrun.shを実行する設定を書きました。

Plaintext
# 毎日 4:00に実行
0 4 * * * /home/sr-exp/sr-experience/doc/download_BackWPup_to_local/run.sh

動作確認

数日後にバックアップのログを見てみると、毎日4時に実行されていることがわかります。

Plaintext
2024-03-23 18:28:30,059 [INFO]: file donwload successful: 2024-03-23_04-24-02_3DI55SUG01.zip
2024-03-23 18:28:36,901 [INFO]: file donwload successful: 2024-03-22_03-22-38_7HI55SWT01.zip
2024-03-23 18:28:42,371 [INFO]: file donwload successful: 2024-03-11_03-40-13_Z7I55SQU01.zip
2024-03-23 18:28:48,655 [INFO]: file donwload successful: 2024-03-10_03-25-53_MLI55SXJ01.zip
2024-03-23 18:28:55,604 [INFO]: file donwload successful: 2024-03-09_03-13-41_YXI55SVZ01.zip
2024-03-23 18:28:55,622 [INFO]: file delete successful: 2024-03-09_03-13-41_YXI55SVZ01.zip
2024-03-23 18:28:55,630 [INFO]: file delete successful: 2024-03-10_03-25-53_MLI55SXJ01.zip
2024-03-23 18:28:55,637 [INFO]: file delete successful: 2024-03-11_03-40-13_Z7I55SQU01.zip
2024-03-23 18:28:55,638 [INFO]: file delete successful: 2024-03-12_03-37-19_FLI55SV501.zip
2024-03-23 18:28:55,639 [INFO]: file delete successful: 2024-03-13_03-10-21_FHI55SSW01.zip
2024-03-24 04:00:08,061 [INFO]: file donwload successful: 2024-03-24_03-27-49_7XI55SV201.zip
2024-03-24 04:00:08,080 [INFO]: file delete successful: 2024-03-14_05-04-13_LPI55SXB01.zip

バックアップファイルもダウンロードできていました。大成功です!!

コメント

タイトルとURLをコピーしました