Ftpディレクトリ全体を並行してダウンロードしようとしています。
#!/usr/bin/python
import sys
import datetime
import os
from multiprocessing import Process, Pool
from ftplib import FTP
curYear=""
remotePath =""
localPath = ""
def downloadFiles (remotePath,localPath):
splitted = remotePath.split('/');
Host= splitted[2]
path='/'+'/'.join(splitted[3:])
ftp = FTP(Host)
ftp.login()
ftp.cwd(path)
filenames = ftp.nlst()
total=len(filenames)
i=0
pool = Pool()
for filename in filenames:
local_filename = os.path.join(localPath,filename)
pool.apply_async(downloadFile, (filename,local_filename,ftp))
#downloadFile(filename,local_filename,ftp);
i=i+1
pool.close()
pool.join()
ftp.close()
def downloadFile(filename,local_filename,ftp):
file = open(local_filename, 'wb')
ftp.retrbinary('RETR '+ filename, file.write)
file.close()
def getYearFromArgs():
if len(sys.argv) >= 2 and sys.argv[1] == "Y":
year = sys.argv[2]
del sys.argv[1:2]
else:
year = str(datetime.datetime.now().year)
return year
def assignGlobals():
global p
global remotePath
global localPath
global URL
global Host
global user
global password
global sqldb
remotePath = 'ftp://ftp3.ncdc.noaa.gov/pub/data/noaa/isd-lite/%s/' % (curYear)
localPath = '/home/isd-lite/%s/' % (curYear)
def main():
global curYear
curYear=getYearFromArgs()
assignGlobals()
downloadFiles(remotePath,localPath)
if __name__ == "__main__":
main()
しかし、私はこの例外を受け取ります:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner
self.run()
File "/usr/lib64/python2.6/threading.py", line 484, in run
self.__target(*self.__args, **self.__kwargs)
File "/usr/lib64/python2.6/multiprocessing/pool.py", line 225, in _handle_tasks
put(task)
TypeError: expected string or Unicode object, NoneType found
この行をコメントアウトすると:
pool.apply_async(downloadFile, (filename,local_filename,ftp)
この行のコメントを削除します。
downloadFile(filename,local_filename,ftp);
その後、それは問題なく動作しますが、低速でマルチスレッドではありません。
更新、2014年5月9日:
私は正確な制限を決定しました。 Pythonのpickle機能 によってオブジェクトをpickle化できる限り、プロセスの境界を越えてオブジェクトをワーカープロセスに送信することができます。元の回答で説明した問題は、ファイルハンドルをワーカーに送信しようとしたために発生しました。簡単な実験で、これが機能しない理由を示します。
_>>> f = open("/dev/null")
>>> import pickle
>>> pickle.dumps(f)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/pickle.py", line 1374, in dumps
Pickler(file, protocol).dump(obj)
File "/usr/lib/python2.7/pickle.py", line 224, in dump
self.save(obj)
File "/usr/lib/python2.7/pickle.py", line 306, in save
rv = reduce(self.proto)
File "/usr/lib/python2.7/copy_reg.py", line 70, in _reduce_ex
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle file objects
_
したがって、このStackOverflowの質問を見つける原因となったPythonエラーが発生した場合は、プロセスの境界を越えて送信するすべてのものをピクルスできることを確認してください。
元の回答:
答えるのが少し遅れています。しかし、Pythonのマルチプロセッシングモジュールを使用しようとしたときに、元の投稿者と同じエラーメッセージが表示されました。このスレッドに出くわした他の人が何かを試すことができるように、私の調査結果を記録します。
私の場合、ワーカーのプールに送信しようとしたものが原因でエラーが発生しました。プールワーカーが噛み砕くためにファイルオブジェクトの配列を渡そうとしていました。 Pythonのプロセス境界を越えて送信するには、明らかに多すぎます。入力ファイル名と出力ファイル名の文字列を指定したプールワーカー辞書を送信することで、問題を解決しました。
したがって、_apply_async
_(私はmap()
とimap_unordered()
を使用しました)などの関数に提供する反復可能オブジェクトには、数値や文字列のリスト、さらには詳細な辞書データ構造(値がオブジェクトでない限り)。
あなたの場合:
_pool.apply_async(downloadFile, (filename,local_filename,ftp))
_
ftp
はオブジェクトであり、問題を引き起こしている可能性があります。回避策として、パラメーターをワーカー(この場合はHost
とpath
のようになります)に送信し、ワーカーにオブジェクトをインスタンス化してクリーンアップを処理させることをお勧めします。