写爬虫时要下载文件,谁知道文件比较大,然后在下载时候总是丢包,因此参考网上的一些源码写了实现分块下载的功能,有能力的童鞋可以自己改写成多线程的。
这个pycurl的分块下载功能我把它封装成了一个类,直接使用即可,接收5个参数,用法如下
d = downloader(downloadurl,out_filePath,None,None,None)
d.download() #调用下载功能下载
完整代码如下:
import pycurl
# 设置curl对象 的proxy reffer cookies
def SetOpt(target_address,proxy=None,reffer=None,cookies=None):
c = pycurl.Curl()
c.setopt(pycurl.URL,target_address)
if proxy != None:
c.setopt(pycurl.PROXY,proxy) # 设置代理
if reffer != None:
c.setopt(pycurl.REFERER,reffer)
if cookies !=None:
c.setopt(pycurl.COOKIE,cookies)
return c
class downloader:
def __init__(self,target_address,out_filePath,reffer,cookies,proxy):
print out_filePath
self.output_file=out_filePath # 输出路径
self.chunk=1*1024*1024 # 设置每次下载的块大小
#创建存放文件的目录
try:
self.dir_name=self.output_file+"tmp"
print self.dir_name
os.mkdir(self.dir_name)
except OSError:
pass
######### 设置CURL对象 ######
self.curl_obj= SetOpt(target_address,reffer=reffer,cookies=cookies)
## 临时的curl对象,用来获取文件的大小
tmp_curl_obj = SetOpt(target_address,reffer=reffer,cookies=cookies)
##### 得到并设置下载文件的大小 ######
tmp_curl_obj.setopt(tmp_curl_obj.NOBODY,True) #获取的文件内容不包含body部分
try:
print "Trying to get size of the file"
tmp_curl_obj.perform()
self.size = tmp_curl_obj.getinfo(tmp_curl_obj.CONTENT_LENGTH_DOWNLOAD) #文件大小
print self.size
except Exception, e:
print e
self.delete_temp()
self.size = 0
#打印进度
# self.curl_obj.setopt(self.curl_obj.NOPROGRESS,1)
self.curl_obj.setopt(self.curl_obj.PROGRESSFUNCTION,self.progress)
##### 下载 ######
def download(self):
if (self.size>0):
print "Starting download. Total size: "+str(self.size)+" bytes or "+str(self.size/1024/1024)+" MB"
else:
print "Starting download"
##### 如果文件大小小于或等于块大小 就直接下载 不用分块了 #####
if self.size <=self.chunk or self.size<0:
self.curl_obj.fp = open(self.output_file, "wb")
self.curl_obj.setopt(pycurl.WRITEDATA, self.curl_obj.fp)
self.curl_obj.perform()
self.curl_obj.fp.close()
return
##### 设置超时时间 #####
self.curl_obj.setopt(pycurl.TIMEOUT,60*10)
log=open("downloader.log","a")
# lim_l 块头 lim_u 块尾 如lim_l=0,lim_u=1023, 则这个块就是 0-1023
lim_l=0
lim_u=self.chunk # 0 - 1M
i=1 #分块编号
###### 下载文件 #####
while lim_l < self.size :
temp_output=os.path.join(self.dir_name,"output"+str(i))
###### 如果该分块已经存在且大小等于块大小1024*1024 说明该分块已经下载完成,继续下一次循环 #####
if os.path.exists(temp_output) and os.path.getsize(temp_output)==self.chunk:
#print "skip chunk ", i, lim_l
i=i+1
r=str(lim_l)+"-"+str(lim_u-1) # 下载的文件分块范围 如 0-(1M-1)、 (1M-(2M-1))....
lim_l=lim_l+self.chunk
lim_u=lim_u+self.chunk
continue
##### 分块没有被下载则开始下载 #####
self.curl_obj.fp = open(temp_output, "wb")
self.curl_obj.setopt(pycurl.WRITEDATA, self.curl_obj.fp)
r=str(lim_l)+"-"+str(lim_u-1)
self.curl_obj.setopt(pycurl.RANGE,r)
print "download chunk", i
##### 下载文件 #####
while True:
##### 下载完成跳出这个循环 #####
try:
self.curl_obj.perform()
self.curl_obj.fp.close()
break
###### 异常则继续下载 #####
except pycurl.error, e:
logmsg = "Pycurl error caught "+str(e)+" while downloading at download range "+str(r)+" while storing to file "+str(temp_output)+"\n"
log.write(logmsg)
print "download {} exception".format(i)
self.curl_obj.fp.close()
self.curl_obj.fp=open(temp_output,"wb")
continue
i=i+1
lim_l=lim_l+self.chunk
lim_u=lim_u+self.chunk
##### 删除下载的临时文件 #####
def delete_temp(self):
i=1
while True:
temp_output=os.path.join(self.dir_name,"output"+str(i))
if os.path.exists(temp_output):
os.remove(temp_output)
else:
break
i=i+1
try:
os.rmdir(self.dir_name)
except Exception, e:
pass
##### 合并文件 #####
def concatenate(self):
##### 合并前清空output_file的内容 #####
fp=open(self.output_file,'wb')
i=1
while True:
temp_output=os.path.join(self.dir_name,"output"+str(i))
if not os.path.exists(temp_output):
break
##### 读取分块内容,依次附加到output_file #####
print "write chunk", i
tp=open(temp_output,"rb")
buf = tp.read(1024 * 1024)
fp.write(buf)
tp.close()
i += 1
fp.close()
#打印进度
def progress(self,download_total,downloaded,uploaded_total,upload):
print "To be downloaded" + str(download_total)
print "Downloaded : " + str(downloaded)