本人前段时间遇到一个问题,就是公司同事给我发来一个“爬虫文件”,需要大概爬取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)