基于python爬虫的百度图片下载器(多线程,可限制下载图片分辨率)

前言

做一个百度图片的爬虫来作为python爬虫的练习,在查阅了相关大佬的博客后,我根据自己的想法写出了这个爬虫

组成部分

多线程获取图片地址

http://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word=XXX&pn=0
上面这个网址格式是我在参考其他人的博客时找到的,用浏览器打开是百度图片可以翻页的形式
在这里插入图片描述
word 是搜索的关键词,每翻过一页 pn 增加20,爬虫依靠这个规律实现。
在这里插入图片描述
通过 requests 库手动获取该 url 的响应内容后,可以发现图片的地址保存在 “objURL” , “middleURL” , “thumbURL” 的内容中。其中 objURL 包含的图片地址可以直接访问。然后通过正则表达式来搜索单页所有的图片 url ,将其保存在一个列表中

s = re.compile(r'"objURL":"(.+?)"')#正则表达式
r = requests.get(home_url,headers = headers,timeout = (5,5))
urls = s.findall(r.text)
pic_urls.extend(urls)#添加图片地址
  • 由于要对所有的图片地址进行搜索计算量很大,所以采用线程池将查找单页所含的图片地址的任务分配给单个线程来加快速度。然后将图片地址汇总再过滤掉重复图片地址
  • 线程池在系统启动时创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数
with ThreadPoolExecutor(max_workers = 50) as pool:#采用50个线程搜索图片地址
		for i in range(300):#默认翻300页
			home_url = root_url + str(i*20)#根据页数获取的网址格式
			pool.submit(get_url,home_url)
	return list(set(pic_urls))#去除重复图片地址

多线程下载图片

同样采用多线程来下载图片,把下载单个图片的任务分配给单个线程。创建文件夹将图片保存在其下,图片名采用其url地址的压缩版,以便未来重复下载时添加新的图片

root = f'D://{keyword}//'#默认保存在D盘,可根据需要自行修改
if not os.path.exists(root):
	os.mkdir(root)#创建存放图片的文件夹
with ThreadPoolExecutor(max_workers = 50) as pool:#采用50个线程
	for pic_url in pic_urls:
		pool.submit(dowload_pic,pic_url,root)
'''单个图片保存'''
suffix = '.gif' if '.gif' in pic_url else '.png' #图片后缀
name = pic_url
for i in ['|','<','>','\\','/',':','?','*','"','.']:
	name = name.replace(i,'')#去除不能出现在文件名中的字符
path = root + name[-100:-1] + suffix
if not os.path.exists(path):#确认是否为新图片
	with open(path,'wb') as f:
		f.write(r.content)

例如爬取CSDN关键词的百度图片:
生成文件夹:
图片存放文件夹
存放的图片:
图片

控制下载图片长宽(可选)

如果想要限制下载的图片的长宽,可以在下载单个图片的代码中加入以下代码:

'''要再导入以下库'''
from PIL import Image#Pillow库需要自行安装
from io import BytesIO

'''添加的代码'''
img = Image.open(BytesIO(r.content))
if img.height>=1000 or img.width>=1000:#筛长或宽大于等于1000的图片
	with open(path,'wb') as f:
		f.write(r.content)

该代码作用是在存储图片前先把图片的二进制内容传到字节流里,然后用 PIL 库打开生成图片对象检测其长宽

完整代码

需要安装requests库
如果要调整下载和搜索的速度,修改相应代码中 max_workers 参数的值即线程的数量

import requests
import re
import os
from concurrent.futures import ThreadPoolExecutor#线程池
import time

headers = {
    
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'}#请求头
count = 0#下载成功图片数
pic_urls = []#存放所有图片的url

def get_url(home_url):#获取单个页面的所有图片地址
	try:
		r = requests.get(home_url,headers = headers,timeout = (5,20))
		r.raise_for_status()
		s = re.compile(r'"objURL":"(.+?)"')
		urls = s.findall(r.text)
	except:
		pass
	else:#无异常发生
		pic_urls.extend(urls)#添加图片地址
		print(f'请耐心等待,搜索到{len(pic_urls):>4}个图片',end = '\r',flush = True)

def get_urls(keyword):#采用多线程获取所有页面图片地址
	root_url = 'http://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word='+ keyword +'&pn='
	with ThreadPoolExecutor(max_workers = 50) as pool:#采用50个线程搜索图片地址
		for i in range(300):#默认翻300页
			home_url = root_url + str(i*20)#根据页数获取的网址格式
			pool.submit(get_url,home_url)
	return list(set(pic_urls))#去除重复图片地址

def dowload_pic(pic_url,root):#下载单个图片
	global count
	try:
		suffix = '.gif' if '.gif' in pic_url else '.png' #图片后缀
		name = pic_url
		for i in ['|','<','>','\\','/',':','?','*','"','.']:
				name = name.replace(i,'')#去除不能出现在文件名中的字符
		path = root + name[-100:-1] + suffix
		if not os.path.exists(path):#确认是否为新图片
			r = requests.get(pic_url,headers = headers,timeout = (5,20))
			r.raise_for_status()
			with open(path,'wb') as f:
				f.write(r.content)
			print(f'{pic_url}下载成功!',flush = True)
			count+=1
	except:
		print(f'{pic_url}下载失败!',flush = True)

def download_pics(pic_urls,keyword):#采用多线程下载图片
	print('\n开始下载')
	print(f'{keyword}:过滤重复图片后总共{len(pic_urls)}个')
	if(len(pic_urls)==0):
		return ''
	root = f'D://{keyword}//'#默认保存在D盘,可根据需要自行修改
	if not os.path.exists(root):
		os.mkdir(root)#创建存放图片的文件夹
	with ThreadPoolExecutor(max_workers = 50) as pool:#采用50个线程
		for pic_url in pic_urls:
			pool.submit(dowload_pic,pic_url,root)

keyword = input('请输入关键词:')
start = time.time()#开始计时
pic_urls = get_urls(keyword)
download_pics(pic_urls,keyword)
input(f'下载完毕!,{count}个下载成功!用时{time.time()-start}s')

运行效果

运行效果

猜你喜欢

转载自blog.csdn.net/weixin_45715159/article/details/104246853
今日推荐