可视化指令的http下载:获取url->循环url->指令http下载 速度大概为:39个url需要20+秒
使用多线程模块,并发20个线程对应20个图片,时间5秒
另有多线程对一文件下载(不建议使用这个多对一对多个下载目标使用,适用较大文件的下载,较多文件,存在文件分段下载之后给它拼回去)
#线程主要运行函数:加信号量锁,主要任务:向目标url发送请求,下载返回的content,请求头设置max-age,与随机模拟的浏览器,避免服务器返回304缓存
def get_http(http_url,semaphore):
#加信号量锁
semaphore.acquire()
header = {'Cache-Control': 'max-age=0','User-Agent': random.choice(USER_AGENTS),}
filename = http_url.split('/')[-1]
respone = requests.get(http_url,headers = header)
filepath = now_path+'\\'+filename
#写入文件
open(filepath,"wb").write(respone.content)
#信号量锁释放
semaphore.release()信号量锁:确保一时间一共设定的线程数对资源进行修改,并且线程完成后释放这个信号量,允许下个进程进入
请求头设计:模拟浏览器,并且设置max-age = 0 避免服务端返回304,下载不到数据
创建文件,并将请求返回主要内容写入
def main(url):
#设定最大线程20个
semaphore = threading.BoundedSemaphore(20)
for i in range(len(url)):
t = threading.Thread(target = get_http,args = (url[i],semaphore,))
#t.setDaemon(True)
t.start()class MulThreadDownload(threading.Thread):
def __init__(self,download_url,path,filename='',download_thread_num=0,part_size=1024*1024):
threading.Thread.__init__(self)
self.download_url = download_url
self.path=path
self.file_name=filename
self.download_thread_num=download_thread_num
self.part_size=part_size
self.file=None
self.threads=[]
self.lock=threading.Lock()
self.session=requests.session()
self.session.mount('http://', HTTPAdapter(max_retries=5))
self.session.mount('https://', HTTPAdapter(max_retries=5))
self.file_size=-1
self.downloaded_size=0
self.taskQ=queue.Queue()
self.mbsize=-1
def download_thread(self,threadid):
while not self.taskQ.empty():
part_dict=self.taskQ.get(block=True,timeout=None)
headers={'Range':'bytes={0}-{1}'.format(part_dict['start'],part_dict['end'])}
response=self.session.get(url=self.download_url,headers=headers)
with self.lock:
self.file.seek(part_dict['start'])
self.file.write(response.content)
self.downloaded_size+=part_dict['end']-part_dict['start']
logging.debug(str(threadid)+' download succeed: '+str(part_dict))
def analysis_filename(self):
# 从url地址中获取文件名
filename = self.download_url.split('/')[-1]
logging.debug('analysis filename form url,got{0},from{1}'.format(filename,self.download_url))
return filename
def run(self):
# 1.从url中获取文件信息,为线程分配下载资源做准备
# 从url提取文件名
logging.info('url:'+self.download_url)
header = {'Cache-Control': 'max-age=0','User-Agent': random.choice(USER_AGENTS),}
# 从文件响应头获取content-length。以及Accept-Ranges字段为分配下载做准备
response_head = self.session.head(self.download_url,headers=header)
if self.file_name == '':
self.file_name = self.analysis_filename()
# if not response_head.headers.has_key('Accept-Ranges'):
if 'Accept-Ranges' not in response_head.headers.keys() :
logging.fatal("不支持断点续传,不支持多线程下载")
self.file_size = int(response_head.headers['Content-Length'])
# 计算文件大小mb值
self.mbsize=round(self.file_size / 1024 , 2)
# 获取文件大小后,创建相同大小的文件
if self.path=='':
filepath=self.file_name
else:
filepath=os.path.join(self.path,self.file_name)
self.file = open(filepath, 'wb')
print(filepath)
self.file.truncate(self.file_size)
# 获得文件大小后划分下载任务。按照part_size进行划分
# 最终分块任务数为,比如说文件大小为1gb,分块1mb,那么就要分成1024份,如果1gb多一点点,那么1025份
part_num = self.file_size//self.part_size+1
# 发送的请求头带上这一条就可以请求指定区间的数据 Range: bytes = 0 - 1048576
# 创建下载队列,把Range的值字符串进行拼接
for num in range(part_num):
start= num*self.part_size
end=(num+1)*self.part_size
if num==part_num-1:
end=self.file_size
#rangestr='bytes={0}-{1}'.format(num*self.part_size,endSize)
part_dict={
'partnum':part_num,
'start':start,
'end':end
}
self.taskQ.put(part_dict)
logging.debug(str(part_dict))
#print(rangestr)
for i in range(self.download_thread_num):
t=threading.Thread(target=self.download_thread,args=(i,))
self.threads.append(t)
t.start()
for t in self.threads:
t.join()
# 全部线程运行结束,说明文件下载完成
self.file.close()
def main(args):
for i in range(len(url)):
mul=MulThreadDownload(download_url= url[i],path = 'C:\\Users\\43881\\Desktop\\ceshi',filename = '' ,download_thread_num=20,part_size = 1024)
mul.start()在请求的返回中截取字段content-length,进行按大小分块
使用多线程去请求下载不同的段落,最终统合一起
统合文件使用了seek()方法,来校正偏移,通过start,end,指定下载的段落
下载多文件存在小bug,欢迎大佬指正。