【python】彼岸图网4K壁纸批量爬虫共1.48G(多线程/多进程)
写在前面
今天我们用python
来编写批量爬取彼岸图网4K壁纸的脚本。
成果展示
目标网站
依赖模块
pip install lxml
pip install requests
pip install beautifulsoup4
pip install fake-useragent
requests
:用于发送请求
beautifulsoup4
:用于解析响应
lxml
:文档解析器,速度快
fake-useragent
:用于生成假的用户代理
请求标头
headers = {
'referer': 'http://pic.netbian.com',
'user-agent': UserAgent().random
}
referer
:爬图片网站一般都会加这个,参数设置为目标网站。
user-agent
:只要是爬虫,一般都需要这个,这里使用fake-useragent
模块生成一个随机值。
彼岸图网其实没啥限制,不加请求标头也请爬。但是最好还是加上,万一人家以后增加反爬机制了呢。
图片分类
tags = [
['4K风景', '4kfengjing'],
['4K美女', '4kmeinv'],
['4K游戏', '4kyouxi'],
['4K动漫', '4kdongman'],
['4K影视', '4kyingshi'],
['4K明星', '4kmingxing'],
['4K汽车', '4kqiche'],
['4K动物', '4kdongwu'],
['4K人物', '4krenwu'],
['4K美食', '4kmeishi'],
['4K宗教', '4kzongjiao'],
['4K背景', '4kbeijing']
]
图片共分为12
类,每一类的url
地址不同。
核心代码
- 获得每类的总页数
page_sum = int(soup.select('div.page>a')[-2].string)
每类图片又分为很多页,每类总页数各不同。通过F12
审查网页:
可以发现,总页数201
类名为page
的div
元素的倒数第三个子元素标签a
中。那么就应该是soup.select('div.page>a')[:-3]
,但实际操作中却发现[:-3]
输出的是201
的上一个a
标签,所以这里就推测最后一个a
标签可能是js
动态加载的,静态爬取不到,所以改为[:-2]
就行了。
- 获取每页所有图片跳转对应的超链接
link_list = soup.select('ul.clearfix>li>a')
imgs_list = [site+link['href'] for link in link_list]
还是F12
审查元素:
可以发现,图片跳转对应的超链接是类为clearfix
的ul
标签的子标签li
的子标签a
的href
属性值。
- 获取单张图片下载对应的直链
img_url = site+soup.select_one('a#img>img')['src']
点击一张图片,跳转到图片的原图下载页面,还是F12
审查元素:
可以发现,图片的下载直链是id
为img
的a
标签的子标签img
的src
属性值。
- 单张图片下载
def save(img_url, img_path):
resp = requests.get(img_url, headers=headers)
with open(img_path, 'wb') as f:
f.write(resp.content)
对比分析
- 多线程
146/146[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]100.00%|190.48S
- 多进程
146/146[>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]100.00%|202.69S
完整代码
- 多线程
import os
import time
import requests
import concurrent.futures as cf
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
# 目标网站
site = 'http://pic.netbian.com'
# 请求头部
headers = {
'referer': site,
'user-agent': UserAgent().random
}
# 图片分类
tags = [
['4K风景', '4kfengjing'],
['4K美女', '4kmeinv'],
['4K游戏', '4kyouxi'],
['4K动漫', '4kdongman'],
['4K影视', '4kyingshi'],
['4K明星', '4kmingxing'],
['4K汽车', '4kqiche'],
['4K动物', '4kdongwu'],
['4K人物', '4krenwu'],
['4K美食', '4kmeishi'],
['4K宗教', '4kzongjiao'],
['4K背景', '4kbeijing']
]
# 保存图片到本地
def save(img_url, img_path):
resp = requests.get(img_url, headers=headers)
with open(img_path, 'wb') as f:
f.write(resp.content)
# 进度条打印
def show(page_num, count, length, runTime):
fin_per = count/length
bar_len = 50
num_fin = round(fin_per*bar_len)
str_fin = num_fin*'>'
num_non = bar_len-num_fin
str_non = num_non*'-'
process = f'{count:0>{len(str(length))}}/{length}[{str_fin}{str_non}]{fin_per*100:.2f}%|{runTime:.2f}S'
print(process, end='\r')
if fin_per == 1.0:
print()
# 下载一页中的所有图片
def down_page(tag_dir, tag_url, page_num):
page_dir = os.path.join(tag_dir, str(page_num).zfill(3))
if not os.path.exists(page_dir):
os.mkdir(page_dir)
else:
pass
if page_num == 1:
page_url = tag_url+'/index.html'
else:
page_url = tag_url+f'/index_{page_num}.html'
resp = requests.get(page_url, headers=headers)
soup = BeautifulSoup(resp.content, 'lxml')
link_list = soup.select('ul.clearfix>li>a')
imgs_list = [site+link['href'] for link in link_list]
for ind, img_link in enumerate(imgs_list):
resp = requests.get(img_link, headers=headers)
soup = BeautifulSoup(resp.content, 'lxml')
img_title = soup.select_one('div.photo-hd>h1').string
img_path = f'{page_dir}\\{ind+1:0>2} {img_title}.jpg'
img_url = site+soup.select_one('a#img>img')['src']
save(img_url, img_path)
# 爬取图片直链并下载
def down(tag_dir, tag_url, page_start, page_end):
startTime = time.time()
tp = cf.ThreadPoolExecutor()
futures = []
count = 0
length = page_end-page_start+1
for page_num in range(page_start, page_end+1):
future = tp.submit(down_page, tag_dir, tag_url, page_num)
futures.append(future)
for future in cf.as_completed(futures):
count += 1
endTime = time.time()
runTime = endTime-startTime
show(page_num, count, length, runTime)
tp.shutdown(wait=True)
# 主函数
def main():
print('彼岸图网4K图片分类为:')
for ind, key in enumerate(tags):
print(str(ind).zfill(2), key[0])
tag_ind = int(input('请选择图片分类的编号: '))
tag_dir = tags[tag_ind][0]
if not os.path.exists(tag_dir):
os.mkdir(tag_dir)
else:
pass
tag_url = site+'/'+tags[tag_ind][1]
resp = requests.get(tag_url, headers=headers)
soup = BeautifulSoup(resp.content, 'lxml')
page_sum = int(soup.select('div.page>a')[-2].string)
page_start = 1
page_end = page_sum
print(f'请图片分类共有页数为: {page_sum}')
page_start = int(input('请选择下载的起始页数: '))
page_end = int(input('请选择下载的结束页数: '))
down(tag_dir, tag_url, page_start, page_end)
if __name__ == "__main__":
main()
- 多进程
将第81行代码:
tp = cf.ThreadPoolExecutor()
改为:
tp = cf.ProcessPoolExecutor()
温馨提示
原图的话非会员用户一天只能下载一次,我们爬的是比原图稍微小一点的中等质量的图像。