Python多线程实现图片批量下载

今天帮朋友写了个批量下载图片的脚本,之前一直以为爬取图片必须要有图片的链接地址,原来请求下载链接也可以实现。话不多说,先上需求:

需求

在一个Excel文件中,存有1800+个链接,每个链接包含一张图片,需实现对这些图片的批量下载。

1551283857844

分析

随便选了一个链接: http://data.rbge.org.uk/herb/E00025608 , 访问该链接,确实能看到一张图片。

1551284098371

F12定位了下图片,直接就能找到这张图的url地址。

1551284351903

.代表当前路径,把它替换成当前浏览器上方显示的url,那完整的链接地址就是https://data.rbge.org.uk/search/herbarium/scripts/getzoom3.php?path=E00025608.zip;file:TileGroup0/0-0-0.jpg

直接访问该链接,是一堆乱码。试着用Python的requests库请求了下这个地址,并将返回内容写入.jpg文件,成功下载。但清晰度堪忧,且朋友对清晰度的要求还挺高,于是又想了下其他方式,发现,图片是可以点击的,跳转到的网站是这样的:

1551285295190

网站下方Get Tiff按钮支持图片的下载,且可以自行选择像素大小。试着点击按钮并抓了下包,发送的请求为

1551285615659

同样用requests模拟这个请求,成功下载。这个请求包含两个参数,base和tier,base可理解为图片id,在Excel文件的链接中能找到;tier表示网站支持选取的七个像素,从0-6递增,6代表5677x8343。那么只要读取到Excel文件中的图片id,再循环模拟请求就能实现批量下载了。

代码

完整代码已上传至GitHub ,这里贴两段关键代码。

读取Excel中的图片id并放入队列

1
2
3
4
5
6
7
8
def get_base_list(self):
wb = load_workbook(self.file_name)
sheet = wb.active
rows = list(sheet.rows)[1:]
for row in rows:
s = re.search(r'http://data.rbge.org.uk/herb/(.*)',row[1].value)
base = s.group(1).strip()
self.base_queue.put(base)

读取Excel使用了openpyxl库的load_workbook方法,使用sheet = wb.active获取当前Sheet,用rows = list(sheet.rows)[1:] 获取所有行,并用切片过滤首行的列头。之后用正则提取图片id并放入队列中,供下载线程的调用。

下载图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def download(self):
while not self.base_queue.empty():
base = self.base_queue.get()
print(f'正在下载图片{base}...')
url = self.download_url.format(base=base,tier=self.tier)

for i in range(3):
try:
response = requests.get(url,headers=self.headers,stream=True)
if response.status_code == 200:
with open(f'{self.save_dir}/{base}_{self.tier}.tiff','wb') as f:
for chunk in response.iter_content(100000):
f.write(chunk)

print(f'图片{base}下载完成。')
time.sleep(random.random())
break

except Exception as e:
print(f'请求出错,地址:{url} 错误:{e} 正在重新请求({i + 1})...')
time.sleep(2)

该函数不断从队列中提取图片id,传入事先定义好的下载链接(之前点击下载按钮抓包抓到的那个链接),请求该链接并将返回内容写入文件,直到队列为空则停止。

之后将此函数作为线程的目标函数即可实现多线程下载。