PythonでFTPによるファイルダウンロードを行う方法

PythonでFTPによるファイルダウンロードを行う方法をご紹介します。

条件

  • Python 3.7.0

サンプルソース

以下がFTP接続して指定ファイルをダウンロードするサンプルです。

import ftplib
from logging import getLogger, StreamHandler, Formatter, DEBUG

## ログ出力設定
logger = getLogger("FTP Test")
logger.setLevel(DEBUG)
stream_handler = StreamHandler()
stream_handler.setLevel(DEBUG)
formatter = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)

## FTP情報
HOST = 'XXX.com'
PORT = 21
USER = 'userName'
PASSWORD = 'password'
DIRECTORY = 'test'

## 取得するファイル名の生成
FILE_NAME = 'test.csv'

## FTP接続、csvファイル取得
logger.debug('== Start FTP ==')
with ftplib.FTP() as ftp:
    try:
        ftp.connect(HOST, PORT)   # ホスト、ポートを指定して接続
        ftp.login(USER, PASSWORD) # ユーザID、パスワードを指定してログイン
        ftp.cwd(DIRECTORY)        # 指定のディレクトリに移動
        with open(FILE_NAME, 'wb') as f:
            ftp.retrbinary('RETR ' + FILE_NAME, f.write) # 対象ファイルをバイナリ転送モードで取得
    except ftplib.all_errors as e:
        logger.error('FTP error = %s' % e)
    else:
        logger.debug('FTP success.')
logger.debug('== End FTP ==')

処理の流れ

  • 処理の流れは以下の通りです。
    • 対象ホストに接続
    • 対象ホストにログイン
    • 指定のディレクトリに移動
    • 対象ファイルをダウンロード

解説

  • ftplibを用いてFTP接続を行っています。
  • withと共に使用することで、明示的にquit()を記述しなくてもftp接続を閉じることが出来ます。
  • ftp.connectでは、「接続先ホスト」と「ポート」を指定して接続を行っています。
  • ftp.loginでは、「ユーザID」と「パスワード」を指定してログインを行っています。
  • ftp.retrbinaryでは、対象ファイルをバイナリ転送モードでダウンロードしています。
    (バイナリ転送モードは、画像などのバイナリファイル取得や、テキストファイルの改行コード変換を行わずに取得したい場合に使用します。)
  • except ftplib.all_errors as e:では、FTP接続で発生する全ての例外を捕獲できます。
    詳細エラーは以下のような種類があります。

    • ftplib.error_reply:サーバから想定外の応答があったときに送出される例外。
    • ftplib.error_temp:一時的エラーを表すエラーコード(400–499の範囲の応答コード)を受け取った時に発生する例外。
    • ftplib.error_perm:永久エラーを表すエラーコード(500–599の範囲の応答コード)を受け取った時に発生する例外。
    • ftplib.error_proto:File Transfer Protocol の応答仕様に適合しない、すなわち1–5の数字で始まらない応答コードをサーバから受け取った時に発生する例外。

サーバー側の設定がおかしい場合の回避策

FTP接続を行うサーバー側の設定がおかしくて、「FTP接続は出来るけれどもデータが取得できない」という状況に出くわす場合があります。

詳細は以下の記事をご参照ください。

FTPで接続は出来るがデータ取得が出来ない場合の対応

回避策ソース

以下のような記述を追記します。

from ftplib import FTP


## ネットワーク設定問題の回避策
_old_makepasv = FTP.makepasv
def _new_makepasv(self):
    HOST, PORT = _old_makepasv(self)
    HOST = self.sock.getpeername()[0]
    return HOST, PORT
FTP.makepasv = _new_makepasv

回避策を含めた全体ソース

全体のソースは以下のようになります。

from ftplib import FTP
import ftplib
from logging import getLogger, StreamHandler, Formatter, DEBUG


## ログ出力設定
logger = getLogger("FTP Test")
logger.setLevel(DEBUG)
stream_handler = StreamHandler()
stream_handler.setLevel(DEBUG)
formatter = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)


## FTP情報
HOST = 'XXX.com'
PORT = 21
USER = 'userName'
PASSWORD = 'password'
DIRECTORY = 'test'

## 取得するファイル名の生成
FILE_NAME = 'test.csv'

## ネットワーク設定問題の回避策
_old_makepasv = FTP.makepasv
def _new_makepasv(self):
    HOST, PORT = _old_makepasv(self)
    HOST = self.sock.getpeername()[0]
    return HOST, PORT
FTP.makepasv = _new_makepasv

## FTP接続、csvファイル取得
logger.debug('== Start FTP ==')
with FTP() as ftp:
    try:
        ftp.connect(HOST, PORT)   # ホスト、ポートを指定して接続
        ftp.login(USER, PASSWORD) # ユーザID、パスワードを指定してログイン
        ftp.cwd(DIRECTORY)        # 指定のディレクトリに移動
        with open(FILE_NAME, 'wb') as f:
            ftp.retrbinary('RETR ' + FILE_NAME, f.write) # 対象ファイルをバイナリ転送モードで取得
    except ftplib.all_errors as e:
        logger.error('FTP error = %s' % e)
    else:
        logger.debug('FTP success.')
logger.debug('== End FTP ==')

参考

Python:ftplib — FTPプロトコルクライアント

https://docs.python.jp/3/library/ftplib.html

コメントを残す

メールアドレスが公開されることはありません。

次の記事

Ubuntuでのcronの使い方