本人前段时间遇到一个问题,就是公司同事给我发来一个“爬虫文件”,需要大概爬取2000多份图片到我们的本地来,但是他的爬虫文件不能满足我的使用,所以为了自己用的方便,所以进行了改造,并使用多线程对其进行了“加持”,一共两套代码,一套是基于cpu核心数自动分配的线程池,一个是可以自定义线程数的多线程,这里我就给大家放的是按照cpu自动分配的代码!
我发现上代码之前大家应该明白两个事情,
1、你需要一定的爬虫基础,需要知道爬虫的基本运作原理
2、你需要知道多线程能解决的事情
总结:python 的多线程只不过是在来回io之间切换线程,而我们需要做的
声明:因为我爬取的是公司的网址,这里就不会给大家看具体的爬取方法,所以如何构造你要的爬取任务是你首先要解决的问题
第一步:构建一个“生产者”,这样说可能很多人都会懵,所以我把他看做是一个“任务池”,就是说你需要提前构建很多个任务,以我这个案例来说,我的任务就是2000个图片需要下载到本地,所以第一步就是要构造2000个任务,因为我们爬取数据不是对数据一无所知,我们爬取的东西都在固定的位置或者实在服务器的固定文件夹,这样只需要我们去将这些需求提取出来就可以了,这里我提取的文件第一步就是一个包含3个元素的文件,其中包含的是
[“图片地址(后半段)”,图片名称,图片地址] * 2000 这一步我称之为任务包
第二步:然后等待系统将这些任务同时执行若干个,以我这个需求来说,就是需要将2000个任务,使用“消费者”也就是能解决这些任务的“特工”程序去解决,在这里的这些特工是自动分配的用的是 from multiprocessing.dummy import Pool as pl 这个代码里面的map()函数,这里不设置就是按照系统分配默认的数量去进行制造“特工”,比如说一般cpu只会使用自己一半的占用率去执行任务,所以我这边大概的线程池有7个左右,也就是说本来2000个任务需要一个进程去做,需要做2000/1次,而现在变成2000/7次,时间效率肯定大大降低,但是这是使用资源换时间的方法,资源的使用率也增加了7倍,这一步一般的up主都成他为“消费者”,也没错,就是概念和理解的问题,我更喜欢理解他为:执行任务的“特工”,这些特工需要做的就是同一类型的事,就是将2000个任务返回的二进制流,保存到本地
废话不多说上代码
import os.path import time from multiprocessing.dummy import Pool as pl import requests
class fileread(object): #一次拿到所有图片的url的后半段地址,这一步就是要构造2000个[“图片地址(后半段)”,图片名称,图片地址] ,然后将其放在一个列表里面此处我省略,返回出去的是一个列表的big_data,大数据包,这里的数据包还不能用,因为这里只包含图片的地址后半段,还需要增加他的域名我放到了下面的函数 def select_Id(self): pass return big_data #数据包,数据包在这里解包,和拼装,组成一个完成的url图片地址,和他的名字,他的存放文件夹[“图片地址(完整)”,图片名称,图片地址],这里返回的就是完整的2000个任务
def select_waubill(self): big_data = fileread.select_Id(self) url_data = [] for i in big_data : url = ("https://www.***.com/"+i[1][1:-1]) name = i[0] supliername = i[2] list_1 = [url,name,supliername] url_data.append(list_1) return url_data #并发请求, def get_url(self,data): try: res = requests.get(data[0],timeout=500) if res: image_data = [res.content,data[1],data[2]] self.save_image(image_data) else: pass print('访问成功',data) except: print('访问没有成功1',data) url2.append(data) print(len(url2)) #单发死循环,直到所有图片下载出 def get_url2(self,data): if len(data[0])<50: pass else: try: res = requests.get(data[0],timeout=500) if res: image_data = [res.content,data[1],data[2]] self.save_image(image_data) else: pass print('访问成功2',data) except: print('访问没有成功2',data) self.get_url2(data) # 保存照片 def save_image(self,data): try: if os.path.exists(save_path + '/' + data[2]): with open(save_path + '/' + data[2] + '/' + data[1], 'wb') as f: f.write(data[0]) else: os.mkdir(save_path + '/' + data[2]) with open(save_path + '/' + data[2] + '/' + data[1], 'wb') as f: f.write(data[0]) except: print('图片已经存在了') if __name__ == '__main__': inside = time.time() # 请求超时的数据放在url2里最后单发重新执行 url2 = [] save_path = "D:/照片中心" # 下载地址 f = fileread() # #数据集合包 big_data2 = f.select_waubill() #并发开始 pool = pl() pool.map(f.get_url,big_data2) pool.close() pool.join() print(f'开始单发请求下载{len(url2)}条') for i in url2: get_url2 = f.get_url2(i) end = time.time() print(end-inside)