我正在尝试并行下载整个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);
然后它工作正常,但它很慢而且没有多线程。
答案 0 :(得分:22)
更新,2014年5月9日:
我确定了确切的限制。只要Python's pickle facility可以对对象进行腌制,就可以跨过程边界将对象发送到工作进程。我在原始答案中描述的问题是因为我试图向工作人员发送文件句柄。一个快速的实验证明了为什么这不起作用:
>>> 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
因此,如果您遇到导致您发现此Stack Overflow问题的Python错误,请确保您跨越流程边界发送的所有内容都可以被腌制。
原始回答:
我回答得有点迟了。但是,在尝试使用Python的多处理模块时,我遇到了与原始海报相同的错误消息。我将记录我的发现,以便其他任何偶然发现这个帖子的人都可以尝试。
就我而言,错误的发生是因为我试图发送给工作池:我试图传递一组文件对象供池工人咀嚼。在Python中发送跨进程边界显然太多了。我通过发送指定输入和输出文件名字符串的池工作者字典来解决问题。
因此,您提供给apply_async
(我使用map()
和imap_unordered()
)等函数的迭代似乎可以包含数字或字符串列表,甚至是详细的字典数据结构(只要值不是对象)。
在你的情况下:
pool.apply_async(downloadFile, (filename,local_filename,ftp))
ftp
是一个对象,可能会导致问题。作为一种解决方法,我建议将参数发送给worker(在这种情况下看起来像host
和path
)并让worker实例化对象并处理清理。
答案 1 :(得分:-1)
你试过了吗?
pool.apply_async(downloadFile, args=(filename,local_filename,ftp))
原型是:
apply_async(func, args=(), kwds={}, callback=None)