大家好我是前端少年汪!痴迷技术,对programming有着极大的兴趣和爱好。
从明天起,做一个新思维的人 继承,多态,层层封装 从明天起,不再关心内存管理 让每一条数据,自动放到合适的位子上 从明天起,我将为每一个对象 取一个温暖的名字 它们用驼峰命名,优雅,大方 陌生人,我也祝福你哈 愿你不再为系统级bug烦恼 愿你在平台之间肆意游荡 愿你不再关心溢出与异常
爬取的目标网站:wallhaven.cc/
使用到的库:
- requests
- Xpath
分析目标网站的DOM结构
我们可以看到这个网站总共分为六个大的模块:Latest,Hot,Toplist,Random,Upload,Forums
我爬取的主要是latest,hot,toplist,random这四个模块的图片.
这四个模块对应的url网址分别为:
- wallhaven.cc/latest
- wallhaven.cc/hot
- wallhaven.cc/toplist
- wallhaven.cc/random
图片的预览页面
这里可以看到page=2的页面总共🈶️24张图片,这个只是缩略图,点击单个item项就可以进入到图片的详情页,可以看到对应的图片的详情页面的url在section>ul>li>figure>a标签的href属性中。
图片的详情页
可以看到真正的图片的url是存在于img标签的src属性,只要我们拿到这个图片的地址就可以下载图片到本地了。
梳理一下逻辑:
大概的逻辑就是这么简单
代码实现
导入相关包,定义全局变量
import os
import threading
import requests
from lxml import etree
"""
爬取网站:https://wallhaven.cc/
"""
# 伪装浏览器请求
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'
}
# 存放下载图片的文件夹名称
new_folder = 'img'
# 5个模块对应的字符串列表
type_list = ['hot', 'toplist', 'latest', 'random']
# 线程池
threads = []
获取缩略图页面
# 单页下载,只下载一页24张图片
def get_wall_one_page(type_index: int, page_num) -> None:
# page_num爬取的页码
url = 'https://wallhaven.cc/{}?page={}'.format(type_list[type_index], page_num)
print(url)
# 获取当前目录
current_dir = os.getcwd()
# 新文件夹的名称
# new_folder = 'img_{}'.format(type_list[type_index]) # 创建新文件夹
if not os.path.exists(new_folder):
os.mkdir(os.path.join(current_dir, new_folder))
res = requests.get(url, headers=headers).text
selector = etree.HTML(res)
img_urls = selector.xpath('//a[@class='preview']/@href')
# 调用获取详情页方法
detail_img(img_urls)
获取图片详情页面
def detail_img(img_urls):
"""
图片详情
:param img_urls:获取到单个图片item的详情url
:return:
"""
i = 0
for item in img_urls:
son_res = requests.get(item, headers=headers).text
selector = etree.HTML(son_res)
src_arr = selector.xpath('//img[@id='wallpaper']/@src')
# 使用多线程下载图片,调用下载方法
t = threading.Thread(target=download_img, args=(src_arr[0], i))
t.start()
threads.append(t)
# download_img(src_arr[0], i)
i += 1
# 等待所有线程完成
for t in threads:
t.join()
print("当前页所有图片下载完成")
下载图片
def download_img(img_src, i):
"""
图片下载
:param img_src: 图片的src
:param i: 序号
:return: void
"""
with open('./{}/{}-{}'.format(new_folder, i, img_src.split('/')[-1]), 'wb') as f:
f.write(requests.get(img_src).content)
print(i, img_src)
以上就是爬取单页(一页24张图片的方法),观察url可以得知,通过改变page这个参数就可以不断爬取不同页码的图片了。可以有2种不同的思路:
如果是第一种的话,我们还需要获取到total总页码数。因为页码是懒加载的,鼠标滑动到底部才会请求加载下一页,这个时候才能看到总页码。
我想到了两种思路:
我感觉两种方法都差不多,就用简单一点的第一种方法
获取总页码
def get_num(type_index):
""" 获取总页码
:param type_index:爬取的类型索引
:return:当前的页码数量
"""
url = 'https://wallhaven.cc/{}?page=2'.format(type_list[type_index])
selector = etree.HTML(requests.get(url, headers=headers).text)
page_num = selector.xpath('//header[@class='thumb-listing-page-header']/h2//text()')
return ''.join(page_num).split(' ')[-1]
爬取指定类型,页码范围or爬取全部页
循环起始页到最后页,无非就是在爬取单页的基础上再多一层循环。
如果不传入开始页码和最后页码的话,那开始页码就是第一页,总页码就是当前页面最后页码
def get_total(type_index, start, end):
"""
爬取指定类型,页码范围
:param type_index:
:param start:
:param end:
:return:
"""
# total = int(get_num(type_index))
if end is None:
end = total
if start is None:
start = 1
try:
for i in range(start, end):
url = 'https://wallhaven.cc/{}?page={}'.format(type_list[type_index], i)
print(url, '开始下载第{}/{}页'.format(i, end - 1))
res = requests.get(url, headers=headers).text
selector = etree.HTML(res)
img_urls = selector.xpath('//a[@class='preview']/@href')
detail_img(img_urls)
# threading.Thread(target=detail_img, args=(img_urls)).start()
except Exception as e:
print(e)
这是获取到的图片结果
最后是所有代码
import os
import threading
import requests
from lxml import etree
"""
爬取网站:https://wallhaven.cc/
"""
# 伪装浏览器请求
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'
}
# 存放下载图片的文件夹名称
new_folder = 'img'
# 5个模块对应的字符串列表
type_list = ['hot', 'toplist', 'latest', 'random']
# 线程池
threads = []
# 单页下载,只下载一页24张图片
def get_wall_one_page(type_index: int, page_num) -> None:
# page_num爬取的页码
url = 'https://wallhaven.cc/{}?page={}'.format(type_list[type_index], page_num)
print(url)
# 获取当前目录
current_dir = os.getcwd()
# 新文件夹的名称
# new_folder = 'img_{}'.format(type_list[type_index]) # 创建新文件夹
if not os.path.exists(new_folder):
os.mkdir(os.path.join(current_dir, new_folder))
res = requests.get(url, headers=headers).text
selector = etree.HTML(res)
img_urls = selector.xpath('//a[@class='preview']/@href')
detail_img(img_urls)
def detail_img(img_urls):
"""
图片详情
:param img_urls:获取到单个图片item的详情url
:return:
"""
i = 0
for item in img_urls:
son_res = requests.get(item, headers=headers).text
selector = etree.HTML(son_res)
src_arr = selector.xpath('//img[@id='wallpaper']/@src')
# 使用多线程下载图片
t = threading.Thread(target=download_img, args=(src_arr[0], i))
t.start()
threads.append(t)
# download_img(src_arr[0], i)
i += 1
# 等待所有线程完成
for t in threads:
t.join()
print("当前页所有图片下载完成")
def download_img(img_src, i):
"""
图片下载
:param img_src: 图片的src
:param i: 序号
:return: void
"""
with open('./{}/{}-{}'.format(new_folder, i, img_src.split('/')[-1]), 'wb') as f:
f.write(requests.get(img_src).content)
print(i, img_src)
def get_num(type_index):
""" 获取总页码
:param type_index:爬取的类型索引
:return:当前的页码数量
"""
url = 'https://wallhaven.cc/{}?page=2'.format(type_list[type_index])
selector = etree.HTML(requests.get(url, headers=headers).text)
page_num = selector.xpath('//header[@class='thumb-listing-page-header']/h2//text()')
return ''.join(page_num).split(' ')[-1]
def get_total(type_index, start, end):
"""
爬取指定类型,页码范围
:param type_index:
:param start:
:param end:
:return:
"""
# total = int(get_num(type_index))
if end is None:
end = total
if start is None:
start = 1
try:
for i in range(start, end):
url = 'https://wallhaven.cc/{}?page={}'.format(type_list[type_index], i)
print(url, '开始下载第{}/{}页'.format(i, end - 1))
res = requests.get(url, headers=headers).text
selector = etree.HTML(res)
img_urls = selector.xpath('//a[@class='preview']/@href')
detail_img(img_urls)
# threading.Thread(target=detail_img, args=(img_urls)).start()
except Exception as e:
print(e)
# https://wallhaven.cc/search?q=code&page=1
# todo 根据输入关键词,查找下载
if __name__ == '__main__':
"""
0:'hot', 热榜
1:'toplist', top排名
2:'latest', 最新
3:'random' 随机
4:'search' 关键字查找
"""
get_wall_one_page(1,1) # 爬取单页
# get_total(2, 1, 11) # 爬取多个指定页码
上面代码还有优化的空间,还可以加入关键词查询爬取,还有标签爬取,条件爬取等等,欢迎评论不同意见
想要壁纸的也可以私信我发你!
喜欢我的小伙伴可以点一波关注。点个赞, 欢迎评论区留言讨论,看到会回复.