1. 项目概述
每次手动下载壁纸是不是很麻烦?作为一个经常更换桌面的Python开发者,我决定写个脚本自动完成这个任务。这个Python脚本能够从指定网站抓取高质量壁纸,并按预设规则保存到本地文件夹。整个过程完全自动化,还能设置定时任务定期更新壁纸库。
这个项目特别适合:
- 喜欢经常更换桌面的用户
- 想学习Python网络请求和文件操作的新手
- 需要批量获取图片素材的设计师
核心功能包括:
- 自动解析壁纸网站页面结构
- 多线程下载提升效率
- 智能命名和分类存储
- 异常处理和日志记录
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案设计
2.1 基础技术栈选择
我选择了最主流的Python技术组合:
- Requests库处理HTTP请求
- BeautifulSoup解析HTML
- concurrent.futures实现多线程
- os和pathlib处理文件操作
为什么不使用Scrapy框架?
虽然Scrapy很强大,但对于这个简单任务显得过于重型。Requests+BS4组合更加轻量灵活,适合初学者理解和修改。
2.2 网站选择与解析策略
经过对比测试,我选择了Wallhaven.cc作为源站,因为:
- 图片质量高(支持4K)
- 无需登录即可下载
- 页面结构清晰稳定
关键解析步骤:
- 分析页面DOM结构,定位图片元素
- 提取高清图链接(通常藏在缩略图属性中)
- 获取图片元数据(分辨率、标签等)
注意:实际开发时要先检查网站的robots.txt文件,确保不违反爬虫规则
3. 核心代码实现
3.1 页面请求与解析
python复制import requests
from bs4 import BeautifulSoup
def fetch_wallpapers(page_url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0) WallpaperDownloader/1.0'
}
try:
response = requests.get(page_url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
return soup.select('figure.preview img')
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return []
3.2 多线程下载器
python复制from concurrent.futures import ThreadPoolExecutor
import os
def download_image(img_url, save_dir):
try:
if not os.path.exists(save_dir):
os.makedirs(save_dir)
filename = os.path.join(save_dir, img_url.split('/')[-1])
if os.path.exists(filename):
return
with requests.get(img_url, stream=True) as r:
r.raise_for_status()
with open(filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"下载完成: {filename}")
except Exception as e:
print(f"下载失败 {img_url}: {e}")
def batch_download(img_urls, save_dir, max_workers=4):
with ThreadPoolExecutor(max_workers=max_workers) as executor:
executor.map(lambda url: download_image(url, save_dir), img_urls)
4. 功能扩展与优化
4.1 自动分类存储
我增加了根据分辨率自动分类的功能:
python复制from PIL import Image
def classify_by_resolution(image_path):
try:
with Image.open(image_path) as img:
width, height = img.size
ratio = width / height
if ratio > 1.7:
return 'wide'
elif 1.3 <= ratio <= 1.7:
return 'standard'
else:
return 'mobile'
except:
return 'unknown'
4.2 定时任务设置
使用Windows任务计划或Linux的cron设置每日自动运行:
bash复制# Linux crontab示例
0 3 * * * /usr/bin/python3 /path/to/wallpaper_downloader.py
5. 常见问题解决
5.1 403禁止访问问题
可能原因和解决方案:
- User-Agent被识别为爬虫 → 添加合法浏览器UA头
- 请求频率过高 → 添加随机延迟(time.sleep)
- IP被暂时封禁 → 使用代理IP轮换
5.2 图片命名冲突处理
改进方案:
python复制import hashlib
def generate_unique_name(img_url):
return hashlib.md5(img_url.encode()).hexdigest() + '.jpg'
5.3 内存占用过高
优化建议:
- 使用stream=True模式下载大文件
- 限制并发线程数量(建议4-8个)
- 及时关闭文件描述符
6. 完整脚本示例
python复制#!/usr/bin/env python3
import os
import time
import requests
import hashlib
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor
from PIL import Image
class WallpaperDownloader:
def __init__(self, save_dir='wallpapers', max_workers=4):
self.save_dir = save_dir
self.max_workers = max_workers
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0) WallpaperDownloader/1.0'
}
def fetch_image_urls(self, page_url):
try:
response = requests.get(page_url, headers=self.headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
return [img['data-src'].replace('small', 'full')
for img in soup.select('figure.preview img')]
except Exception as e:
print(f"获取图片链接失败: {e}")
return []
def download_image(self, img_url):
try:
category = self._get_image_category(img_url)
save_path = os.path.join(self.save_dir, category)
os.makedirs(save_path, exist_ok=True)
filename = os.path.join(save_path,
hashlib.md5(img_url.encode()).hexdigest() + '.jpg')
if os.path.exists(filename):
return
with requests.get(img_url, stream=True, headers=self.headers) as r:
r.raise_for_status()
with open(filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"下载完成: {filename}")
time.sleep(0.5) # 礼貌性延迟
except Exception as e:
print(f"下载失败 {img_url}: {e}")
def _get_image_category(self, img_url):
# 实现分类逻辑
return 'general'
def run(self, page_urls):
all_urls = []
for url in page_urls:
all_urls.extend(self.fetch_image_urls(url))
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
executor.map(self.download_image, all_urls)
if __name__ == '__main__':
downloader = WallpaperDownloader()
downloader.run([
'https://wallhaven.cc/search?categories=111&purity=100&sorting=random'
])
7. 使用建议与注意事项
- 控制下载频率,建议每天不超过100张
- 最好在非高峰时段运行脚本(如凌晨)
- 定期清理重复图片(可以使用imagededup库)
- 尊重版权,不要用于商业用途
- 考虑添加代理支持以防IP被封
我在实际使用中发现,设置合理的User-Agent和请求间隔可以大幅降低被封风险。另外,建议将下载的图片按主题或颜色分类,方便后续查找使用。
