【Python】ラズパイでBackWPupのバックアップファイルをFTPでダウンロードするプログラムを作ってみた

電子工作・プログラム勉強中
スポンサーリンク
スポンサーリンク

WordPressププラグイン「BackWPup」で定期的にバックアップ取っているが、もしサーバー障害でデータが飛んだらバックアップの意味がない。「BackWPup」にはFTPで他のサーバーにアップロードする機能があるが、当ブログで借りているリトルサーバーにはPHPのFTPが無いのでアップロードできない。

「BackWPup」のFTP項目にFTP関数「ftp_nb_fput」が不足していると表示されているが、多分手作業でモジュールとか入れると使えるかもしれない。ただ、方法が分からないのとリトルサーバーに入るかが不明確。

なので、ラズベリーパイとPythonで定期的にFTPでバックアップファイルをダウンロードするプログラムを作ることにした。

ラズベリーパイを使う理由は、常時パソコンを稼働させるわけにはいかないのと、環境構築が面倒だから。Pythonはこのブログに公開してる管理人宅の室内温度表示で使ってるのもあってサンプルコードの組み合わせであるものの、まだ慣れている部分がある。

作りながら調べながら途中経過など、今後のメモ用に書いているがプログラムはとりあえず動けばよいというレベルなので、ご了承いただきたい。

追記1:BKファイルの世代管理で、サーバーからデータが消えたらローカルでもBKファイル削除するようになってたのでプログラム修正(2018/11/26)

追記2:世代管理で削除するファイルが順不同になってたのでファイルリストを昇順にして古いファイルを削除するように修正。

スポンサーリンク
スポンサーリンク

FTPでファイルダウンロードする際のプログラムの課題

といってもバリバリとプログラムが書ける技術は無いので、ネット上にあるサンプルコードを参考に組み合わせて何とか動かすことにする。

やりたいこと

  • FTP接続でサーバーからファイルダウンロードする
  • ローカル(ラズパイ)に保存したファイルの世代管理をする
  • ブログのURL一覧リストが書いてる外部ファイルからブログ毎のバックアップファイルをDL

FTPでファイルを取得しないと始まらない。BackWPupで定期実行してるバックアップファイル(以後BKファイル)は15日分の世代管理しているので、ローカルにダウンロードしたBKファイルも同じ日数分を世代管理する。

リトルサーバーでは複数サイトのWordPressを運用してるので、URLリストを書いたテキストファイルからリトルサーバー内のWordPress毎の「BackWPup」で作成されたバックアップデータをダウンロードする。FTPのアカウントは1つで行う。外部ファイルにURLリストを書く理由は、管理のしやすくするのと、WordPressのサイトの件数が変わっても対応出来るようにするため。

下は、FTPでダウンロードしたバックアップデータ保存先階層のイメージ。

/bk
 |--URL_List.txt #サイトURLとBKファイルのディレクトリパスの一覧
 |--/ccoronblog.net
 |   |--bk-1.zip
 |   |--bk-2.zip
 |   :
 |--/URL2
 |   |--bk-1.zip
 |   |--bk-2.zip
 |   :

「URL_List.txt」の中身は「URL,FTPのバックアップ保存先フルパス」となっている。

また、ローカルのディレクトリも、ブログ毎にURLでディレクトリを分けて保存したい。BKファイルのファイル名は世代管理をやり易くするためサーバー側と同一にする。

ちなみに、ラズベリーパイのディスク容量は32GBのマイクロSD使用で、バックアップファイル無しの状態で残り16GBほど。

やりたいことからプログラムを考える

  • URLリストの外部ファイルからURLを取得して配列に入れる
    • ローカルにあるバックアップデータのファイルリストを取得
    • FTPでサーバーにあるバックアップデータのファイルリストを取得
    • ローカルのファイル名とサーバーのファイル名を比較(照らし合わせる)
    • ローカルに存在しないファイルのみサーバーからダウンロードする
    • ローカルにあるファイル件数が15件を超えたら古いファイルを削除する
  • URLリスト分を繰り返す

このような流れ。

プログラムを書く上で現時点で不明なのが

  • ローカルのディレクトリ内のファイル一覧を取得
  • ファイル取得を更新日(昇順)で出来るか
  • FTPでサーバーのディレクトリ内のファイル一覧の取得
  • FTPで指定したファイルをダウンロード
  • 外部ファイルを読み込んで配列化する
  • ローカルのファイル操作(削除)
  • foreachの使い方

この辺りが課題になるので一つ一つやっていく。ただ、太字が一番重要かと思ってて、これが出来たら他はすんんり進むと思ってる、

いきなり外部ファイルから取得したサイト件数分を書こうとすると頭がこんがらがるので、まずは1サイトだけ書いた外部ファイル取得、1BKファイルのダウンロード、そして1サイト内のBKファイル全件ダウロード、世代管理。

それが出来ると、サイト件数分繰り返すように処理すれば多分行けると思う。

なので、上記リストのプログラム処理を一つ一つ調べながら作っていく。

Pythonのプログラムの課題で調べたこと

ここからは、作りながら調べたことを書いている。

外部ファイルを読み込んでリスト化する

外部ファイルにはURLとサーバ側のBKファイル保存先のフルパスを書き込んでいる。

coronblog.net,パス
URL2,パス
:

外部ファイルを読み込むのと「改行」で分割する方法はこちらを参考にした。

pythonでファイルの読み書き - Remrinのpython攻略日記
pythonでファイルの読み書きです。 python3で外部ファイルを開くときはcodecsモジュールは不要です。 パスが通ったディレクトリにhyaku.txtがあることを前提にしています。 なければ、メモ帳などで適当に複数行の文字を書き込んだりで。 今回は百人一首を書き込んだテキストファイルを準備しました。 # co...

確認用コード

with open('url_list.txt', 'r') as f:
    url_list = f.read()
url_list = url_list.split('\n') #改行で分割
print(url_list)
print(len(url_list))

結果

['url1,パス1', 'url2,パス2']

面倒くさいけど、一つ一つ出力結果を確認しながら進めていく。なお、URLやパスなどは実際の内容と変えている。

改行で分割してリスト化出来た。ただ、URLとパスも分割しないと行けない。

for文で「url_list[n]」を配列のようにして、さらに「split」でURLとパスの「,」を分割しようとしたけどエラーでうまくいかない。

さらに調べると[split]メソッドで複数分割するには[re]モジュールを使えば良い事が分かった。

【Python】 区切り文字を複数指定して文字列を分割する - 旅行好きなソフトエンジニアの備忘録
C#のSplitメソッドと異なりPythonのSplitメソッドは区切り文字を一つしか指定できないようです。区切り文字を複数指定したい場合はreモジュールを利用する必要があります。 import re str = 'a,b.c_d-e' re.split('', str) # と出力される

確認用コード

import re

with open('url_list.txt', 'r') as f:
    url_list = f.read()
url_list = re.split('[\n,]', url_list) #改行とカンマで分割
print(url_list)
print(len(url_list))

結果

['url1', 'パス1', 'url2', 'パス2']

「リストの偶数 = URL、リストの奇数 = サーバ側バックアップのパス」になったので、とりあえずURLとパスで分割できるようになった。

ローカルのディレクトリ内のファイル一覧を取得

次に、ローカルのディレクトリ内のファイルをリストで取得する。リストで取得できれば、ローカルとサーバ側のBKファイルの「照らし合わせ」が出来るようになる。

ディレクトリ内のファイルのリストを取得するにはここで確認

pythonでフォルダ内のファイル一覧をパス無しで取得する - Qiita
pythonでフォルダの中を見る時ファイル名、ディレクトリ名は欲しいけどパスは要らないって場合には何が一番よいやり方なのでしょうね... 例えばtestディレクトリに以下の様なファイルが存在するとして test.csv te...

「glob.glob()」を使うとフォルダまでパスに入ってたので、この中の「os.listdir()」を使用。ワイルド―カードが使えませんが、フォルダ内はBKファイルだけなので問題ないでしょう。

ファイル取得を更新日(昇順)で出来るか

[os.listdir()]の並び順を調べたら、どうやら不定らしい。

pythonのosライブラリのos.listdir()で格納したファイル名の順番の仕組みがわからない。|teratail
pythonのosライブラリのos.listdir()を使用して、ファイル名をlist型変数に格納しようとしています。しかし、格納される順番の仕組みが分かりません。何を指標として格納する順番を決めているのでしょうか?例えば、 import osfile_name = os.listdir("/ho

次にリストのソートについて調べる。

【Python入門】list sortでリストの中身を効率的にソートする方法 | プログラミング教室情報サイト【プロナビ】
プログラミングをしていると、リストの中身をソートしたい場面が出てきます。言語によってはfor文などを駆使して、既存のソートアルゴリズムを実現する、というやり方のものがあり、意外と大変です。しかし、Pythonならリストをソートするための関数が用意されているため、簡単にソートすることができます。 今回は、リストの中身をソ...

[.sort()]を使えば昇順になるらしい。つまり、一番古いファイル名がリスト[0]になる。これで、世代管理で古いファイル削除するのとサーバにあるBKファイルとの比較が出来るようになった。

FTPでサーバーのディレクトリ内のファイル一覧の取得

FTPの接続は以前書いたプログラムからコピペ。

ブログに管理人宅の室温を表示する「誰得?」な機能を付けてみた
手元にRaspberryPiがあってローカルで「ファイルサーバーやWordPressのテストとしてWEBサーバー」としても使ってるのだけど、ラズパイの特徴でもある電子工作的な使い方してなかったので。管理人宅の部屋の温度をブログに表示させるこ...

サーバーのディレクトリ内のファイル位置らの取得はこちらを参考。

Python ftplibでFTPサーバからダウンロード | fukuの犬小屋
PythonでFTPサーバからファイルをダウンロードします。 HTTPリクエストでよく使うrequestsはFTPに対応していません。 ftplibというライブラリを使用します。 ディレクトリ内のファイルを全てダウンロード ftplibは標準ライブラリなのでpipなど使わずにそのままインポートできます。 今回は例として...

FTPのファイルリスト取得は関数にした

def ftp_cwd_list(dir_var): #dir_varはサーバーのディレクトリ
    # 接続先サーバーのホスト名
    ftp = ftplib.FTP("ホスト名")
    ftp.set_pasv("true")
    # ユーザ名とパスワードを指定しログイン
    ftp.login("ユーザーアカウント", "パスワード")
    ftp.cwd(dir_var)
    file_list_var = ftp.nlst('*.zip') #ワイルドカードで拡張子ZIPだけ抽出
    ftp.close()
    return file_list_var

ファイル一覧が取得できた。昇順されているようだが、一応は[.sort()]で昇順に。

ログイン(接続)・ファイル一覧取得・ファイルダウンロード・切断を分けて使おうとしたが「ftp」が関数の外で使ってないから関数分けると定義されてないなんとかでエラーが出た。まあグローバル変数で「ftp = ftplib.FTP(“ホスト名”)」を定義すればいいんだけど、書くのが面倒なので1回1回接続切断することにした。

FTPでファイルをダウンロード

ローカルディレクトリ保存先パス、サーバ側ディレクトリパスの指定が分かんなかった。ローカルディレクトリにBKファイル作成されたから、てっきりダウンロードしたかと思ったけどファイルの中身が空(0KB)。処理もタイムアウトになっていた。

FTPからダウンロードしたら、そのファイル名で保存してくれるわけでは無いようだ。ローカルのディレクトリに「ファイル作成」して、そのファイルにダウンロードしたファイルを保存するみたい。

def ftp_file_dl(local_dir_var, ftp_dir_var, file_name): #ファイルダウンロード
    # 接続先サーバーのホスト名
    ftp = ftplib.FTP("ホスト名")
    ftp.set_pasv("true")
    # ユーザ名とパスワードを指定しログイン
    ftp.login("ユーザーアカウント", "パスワード")
    ftp.cwd(ftp_dir_var) #ディレクトリ移動
    with open(dir_pass + local_dir_var + '/' + file_name, 'wb') as f:
        ftp.retrbinary('RETR %s' % './' + file_name, f.write)
    ftp.close()

FTPのファイル一覧取得と同じサイトを参考。

foreachの使い方

ローカルとサーバーにあるBKファイルで同一名の重複チェックは「繰り返し」になるのでfor文が必要だ。その中でサーバーにしか存在しないBKファイルをリストに追加する。ダウンロードするBKファイル名をリスト化することでローカルに存在しないファイルだけ、サーバーからBKファイルをダウンロードできるようになる。

ただ、Pythonには「foreach」は無いようだ。なので、for文を使う。まずはFTPで取得したファイル一覧を抽出する。

サーバーにあるローカルに存在しないBKファイルを全てダウンロードする

まずは、for文でファイル名抽出するの動作確認。

for file_name in ftp_file_name:
    print(file_name)

意外と簡単だった。なるほど、「ftp_file_name」変数のリストを1つ1つ取り出して「file_name」の変数に入れている。

C言語にあるような「print(file_name[i])」みたいな記述は不要。リストが全て抽出するとループから抜けるもよう。

これで取得したファイル一覧や外部ファイルのURLの個数が変則的でも対応できそう。ただ、ローカルとサーバーにあるBKファイルの照らし合わせは出来なさそうなので考えてみる。

やりたい処理はこれ

  • ローカルBK0:サーバーなし
  • ローカルBK1:サーバーBK1
  • ローカルBK2:サーバーBK2
  • ローカルなし:サーバーBK3

両者のBKファイルを比較してサーバー側にしかないBKファイル(この場合はBK3)のみダウンロードしたい。

forで繰り返してif文で両者のリストを比較しようと思ったが、このサイトでも書いてあるように簡単にリストの比較が出来ることが分かった。

リストとリストを比較して、共通する要素をリストで取り出す方法 - Qiita
文字列を要素とする2つのリストを比較して、共通する要素をリストとして取り出したいことがありました。 まあ、出来るんですが、どうやるのが良いのかなと自分なりにアレコレ考えたり調べたりしました。 ###たとえば、 こんな、tag_li...

ただ、比較して共通しないBKファイルを抽出したいので、「みんなのPython」を参考に差分で抽出する。

local_name_set = set(local_dir_list) #ローカルのBKファイルのリスト
ftp_name_set = set(ftp_file_name) #サーバーのBKファイルのリスト
new_file_name = list(ftp_name_set -local_name_set) #サーバにしか存在しないファイル取得
old_file_name = list(local_name_set - ftp_name_set) #ローカルにしか存在しないファイル取得

new_file_name.sort()
old_file_name.sort()

それぞれ、「ローカルにしか存在しないBKファイル」、「サーバーにしか存在しないBKファイル」を比較している。リストの並び順が不定だったのでソートで昇順にしてる。

2つ用意することで、「サーバーからダウンロードするBKファイル」、「ローカルで削除する古いBKファイル」が出来ることになった。

これを利用して、先ほど作ったダウンロード関数からローカルに存在しないBKファイルを全てダウンロードするようにする。

if len(new_file_name) != 0: #リストが空ではない
    for new_file_name in new_file_name: #ローカルに存在しないBKファイルを全てDL
            ftp_file_dl(url_list[0], url_list[1], new_file_name) #FTPでダウンロード

このように追加した。「url_list[]」はそれぞれ、サイトURLとBKファイルのフルパス。まだ、1サイトでの動作確認。

サイト毎に繰り返す

先ほどループでローカルに存在しないBKファイルを全てダウンロードする処理を作りましたが、それを複数サイトでダウンロードするようにしました。

url_cnt = len(url_list) #サイトの件数
for  i in range(0,url_cnt,2): #1つ飛び出ループ
    if not os.path.isdir(url_list[i]):
        os.mkdir(url_list[i]) #サイト毎にBKフォルダ作成
    :

読み込んだサイト一覧の外部ファイルのリストの件数分繰り返します。ただし、リストは「URL,BKファイルのパス,URL,BKファイルのパス,…」になてるのでループは1つ飛びにしてます。

ついでにローカルのサイト別のフォルダ作成。

フォルダの作成と存在の確認はこちらを参考

Pythonでディレクトリ(フォルダ)を作成するmkdir, makedirs | note.nkmk.me
Pythonで新しいディレクトリ(フォルダ)を作成するには標準モジュールosを使う。以下の二つの関数が用意されている。新しいディレクトリを作成: os.mkdir() 深い階層のディレクトリまで再帰的に作成: os.makedirs() Python3.4以降ではパスをオブジェクトとして操作できるpathlibモジュー...

サイト毎のループに今まで作った処理(FTPでBKファイルダウンロード)を行います。確認用で1サイトだけだったのを複数サイト対応にも修正。

ローカルのファイル操作(削除)

最後に、BKファイルのダウンロードが終わったら古いBKファイルを削除します。ローカルのファイルリストとサーバーのファイルリストの差分から「ローカルに存在していてサーバーに存在しないBKファイル」を削除します。

これで、サーバーにあるBKファイルとローカルにあるBKファイルの同期が取れるはずです。

for old_file_name in old_file_name:
        print('ダウンロード:' + old_file_name)
        os.remove(url_list[i] + '/' + old_file_name)

ローカルにしか存在しないBKファイル「old_file_name」を削除します。サーバーに無いファイルをローカルで全て削除してますが、サーバーのBKファイルのディレクトリと同じファイルにすることで、ローカルのBKファイルを世代管理しています。

サーバー側のBKファイルの世代管理はWordPressプラグイン「BackWPup」で管理してます。

ファイルの削除はこちら参考

ファイルやフォルダーを削除する - Python Tips

動作確認

一度ダウンロードしたBKファイルを古いファイルと見立てて名前を変更して、もう一度ダウンロード。名前を変更したBKファイルはサーバーには存在しないのでローカルでも削除される。

既にダウンロード済みのBKファイルも再びダウンロードしないようにもなっている。URLリストに新たにURLを追加したサイトのBKファイルもダウンロードできている(同一サーバー内に限り)。

全ソース

改めておさらい。

  • 同一サーバー内のサイト毎のBKファイルをダウンロードする
  • ローカルとサーバーのBKファイルの差分から世代管理する

ローカルに保存してるBKファイルの世代管理はファイル数で行う予定だったが、サーバーのBKファイルとの差分を取る方法に変更した。処理としてはサーバーのバックアップディレクトリに無いファイルを削除する。

#!/usr/bin/env /usr/bin/python
# -*- coding: utf-8 -*-

import re
import os
import glob
import ftplib

dir_path = 'バックアップファイル置くディレクトリ' #絶対パス

#サーバーのBKファイル一覧取得関数
def ftp_cwd_list(dir_var):
    # 接続先サーバーのホスト名
    ftp = ftplib.FTP("ホスト名")
    ftp.set_pasv("true")
    # ユーザ名とパスワードを指定しログイン
    ftp.login("ユーザーアカウント", "パスワード")
    ftp.cwd(dir_var)
    file_list_var = ftp.nlst('*.zip')
    ftp.close()
    return file_list_var

#BKファイルダウンロード関数
def ftp_file_dl(local_dir_var, ftp_dir_var, file_name):
    # 接続先サーバーのホスト名
    ftp = ftplib.FTP("ホスト名")
    ftp.set_pasv("true")
    # ユーザ名とパスワードを指定しログイン
    ftp.login("ユーザーアカウント", "パスワード")
    ftp.cwd(ftp_dir_var) #ディレクトリ移動
    with open(local_dir_var + '/' + file_name, 'wb') as f:
        ftp.retrbinary('RETR %s' % './' + file_name, f.write)
    ftp.close()


os.chdir(dir_path) #ローカルのディレクトリ移動

#サイトリストを取得(外部ファイル)
with open('url_list.txt', 'r') as f:
    url_list = f.read()
url_list = re.split('[\n,]', url_list)
url_cnt = len(url_list)

#サイト毎に処理開始
for  i in range(0,url_cnt,2): #1つ飛び出ループ
    print(url_list[i])

    #サイト毎にディレクトリ作成
    if not os.path.isdir(url_list[i]):
        os.mkdir(url_list[i])

    ftp_file_name = ftp_cwd_list(url_list[i+1])
    local_dir_list = os.listdir(url_list[i])
    local_dir_list.sort()

    local_name_set = set(local_dir_list)
    ftp_name_set = set(ftp_file_name)
    new_file_name = list(ftp_name_set -local_name_set) #サーバにしか存在しないファイル取得
    old_file_name = list(local_name_set - ftp_name_set) #ローカルにしか存在しないファイル取得

    #リストを昇順にソート
    new_file_name.sort()
    old_file_name.sort()
    if len(new_file_name) != 0:
        for new_file_name in new_file_name:
            print('ダウンロード:' + new_file_name)
            ftp_file_dl(url_list[i], url_list[i+1], new_file_name)
    else: #新しくダウンロードするファイル無し
        print('新規BKファイル無し')
    for old_file_name in old_file_name:
        print('ファイル削除:' + old_file_name)
        os.remove(url_list[i] + '/' + old_file_name)

FTPでBKファイルダウンロード中は例外処理していませんが、とりあえず動作したので一応完成。

後は、「BackWPup」でバックアプ取った1~2時間後ぐらいに、ラズベリーパイのcronで定期的に実行するだけです。

反省点

実は、半年以上前にPython入門書「みんなのPython」を購入している。ただ、買っただけでほとんど読んでいない。

購入の理由は、Pythonで文字コードを扱うのがややこしかったため。色々な入門書の中から文字コードの扱ってるページ数が多いこれにした。

今回のプログラム作る際に、ググって色々なサイトを参考にしたが、FTP以外の「リストの昇順、リストの比較(差分)、ファイル・フォルダの操作」などほとんどが、この本1冊に書いてある内容だった。

なので、見よう見まねで作ったものの、本1冊勉強していればもう少しすんなりと作れたのではと思う。基礎が大事だと改めて感じたので、今後はちゃんと本1冊勉強していきたい。

世代管理のプログラムを修正(2018/11/26)

上記プログラムはローカルとサーバーにあるBKファイルのファイル名不一致から世代管理していたけど、これだと何らかの影響でサーバーからデータが消えた場合はローカルのBKファイル全て消えることになる。

なので、ローカルに保存されているファイル数で世代管理することにプログラム修正した。

ただ、プログラムは追加修正しただけなので効率悪いです。

あと、BKファイルの世代管理を[ローカルの世代管理数<サーバーの世代管理数]にすると、次回余計にダウンロードしてローカルで削除します。

例:初回プログラム実行で、ローカルの世代管理10で、サーバーのBKファイル15個だと

  1. サーバーからBKファイル15個ダウンロードする
  2. ローカルにある古いBKファイル5個削除

2回目以降プログラム実行

  1. サーバーから新規BKファイル5個ダウンロードする
  2. ローカルにある古いBKファイル5個削除

になります。

追記2:世代管理でファイルリストが順不同になってたので、昇順するように修正。削除するファイルも頭から「順不同」で、ローカル側に古いBKファイルが残っていたのと新しいBKファイルを削除して、次回も同じBKファイル(ローカルにファイルが存在しないため)をダウンロードするようになっていました。

#!/usr/bin/env /usr/bin/python
# -*- coding: utf-8 -*-

import re
import os
import glob
import ftplib

dir_path = 'バックアップファイル置くディレクトリ' #絶対パス
file_gen = 15 #BKファイルの世代管理の上限数

#サーバーのBKファイル一覧取得関数
def ftp_cwd_list(dir_var):
    # 接続先サーバーのホスト名
    ftp = ftplib.FTP("FTPのホスト名")
    ftp.set_pasv("true")
    # ユーザ名とパスワードを指定しログイン
    ftp.login("FTPユーザーアカウント", "パスワード")
    ftp.cwd(dir_var)
    file_list_var = ftp.nlst('*.zip') #拡張子はBackWPupと合わせる
    ftp.close()
    return file_list_var

#BKファイルダウンロード関数
def ftp_file_dl(local_dir_var, ftp_dir_var, file_name):
    # 接続先サーバーのホスト名
    ftp = ftplib.FTP("FTPのホスト名")
    ftp.set_pasv("true")
    # ユーザ名とパスワードを指定しログイン
    ftp.login("FTPのユーザーアカウント", "FTPのパスワード")
    ftp.cwd(ftp_dir_var) #ディレクトリ移動
    with open(local_dir_var + '/' + file_name, 'wb') as f:
        ftp.retrbinary('RETR %s' % './' + file_name, f.write)
    ftp.close()


os.chdir(dir_path) #ディレクトリ移動

#サイトリストを取得(外部ファイル)
with open('url_list.txt', 'r') as f:
    url_list = f.read()
url_list = re.split('[\n,]', url_list)
url_cnt = len(url_list)

#サイト毎に処理開始
for  i in range(0,url_cnt,2): #1つ飛び出ループ
    print(url_list[i])

    #サイト毎にディレクトリ作成
    if not os.path.isdir(url_list[i]):
        os.mkdir(url_list[i])

    ftp_file_name = ftp_cwd_list(url_list[i+1])
    local_dir_list = os.listdir(url_list[i])
    local_dir_list.sort()

    local_name_set = set(local_dir_list)
    ftp_name_set = set(ftp_file_name)
    new_file_name = list(ftp_name_set -local_name_set) #サーバにある新しいファイル取得

    #リストを昇順にソート
    new_file_name.sort()
    if len(new_file_name) != 0:
        for new_file_name in new_file_name:
            print('ダウンロード:' + new_file_name)
            ftp_file_dl(url_list[i], url_list[i+1], new_file_name)
    else: #新しくダウンロードするファイル無し
        print('新規BKファイル無し')
    #ここからBKファイルの世代管理
    local_dir_list = os.listdir(url_list[i])
    local_dir_list.sort()
    if len(local_dir_list) > file_gen:
        file_cnt = len(local_dir_list) - file_gen
        for n in range(file_cnt):
            print('ファイル削除:' + local_dir_list[n])
            os.remove(url_list[i] + '/' + local_dir_list[n])
    else:
        print('削除ファイルなし 世代:' + str(len(local_name_set))) #ローカルのBKファイルの数表示