1. 项目概述从零构建一个高效的Wallheaven壁纸爬虫最近在整理自己的素材库发现高质量的壁纸资源总是零零散散尤其是像Wallheaven.cc这类以海量、高分辨率、社区精选著称的壁纸网站手动一张张下载效率实在太低。作为一个经常需要新鲜视觉刺激的程序员兼设计爱好者我决定动手写一个爬虫把这件事自动化。这不仅仅是一个简单的“请求-解析-下载”流程更涉及到对现代反爬策略的应对、大规模文件的高效管理以及如何优雅地处理可能出现的各种异常。今天我就把自己从零搭建这个爬虫的完整思路、踩过的坑以及最终稳定运行的方案分享出来无论你是Python新手想练手还是老手在寻找一个具体的实战案例相信都能从中找到有用的东西。简单来说这个项目就是利用Python爬虫技术自动化地从Wallheaven网站批量下载指定类别、分辨率或收藏夹的壁纸。它的核心价值在于解放双手让你能一次性获取成百上千张精心筛选的4K/8K壁纸用于个人收藏、设计参考或创建本地壁纸库。接下来我会详细拆解整个过程的每一个环节。2. 核心思路与架构设计2.1 目标网站分析与策略制定在动手写代码之前花时间分析目标网站的结构和行为模式是至关重要的这能避免后期很多不必要的麻烦。Wallheaven.cc是一个相对友好的网站但它依然具备一些基本的反爬机制。首先我通过浏览器开发者工具F12分析了其页面加载和图片呈现的逻辑。Wallheaven的壁纸列表页例如按最新、热门、随机排序的页面采用了经典的“瀑布流”加载方式。当你滚动页面时会通过XHR请求动态加载新的图片数据返回的是JSON格式。这对于爬虫来说其实是好事因为我们可以直接模拟这些JSON请求高效地获取结构化数据而无需解析完整的HTML。关键发现点在于其API接口。观察网络请求后我发现了一个规律列表页的翻页或加载更多操作通常会请求一个类似https://wallhaven.cc/api/v1/search?page2的接口。这个接口返回的JSON里包含了当前页所有壁纸的详细信息如唯一ID、分辨率、文件大小、预览图地址以及最重要的——原图下载地址。另一个重点是图片的实际下载链接。Wallheaven的图片存储在CDN上链接模式通常是https://w.wallhaven.cc/full/{图片ID的前两位字符}/{图片ID}.jpg或其他格式如png。例如ID为z8e3x1的图片其原图链接可能就是https://w.wallhaven.cc/full/z8/e8e3x1.jpg。了解这个规则我们甚至可以在某些情况下绕过页面直接构造下载链接。注意直接、高频地请求其API或下载链接依然可能触发频率限制或临时封禁。因此我们的策略必须包含合理的延时、请求头伪装以及错误重试机制。2.2 技术栈选型与工具准备基于以上分析我选择了以下技术栈它们都是在Python爬虫生态中久经考验的组合请求库requests这是Python中最简单易用的HTTP库。相比于urllib它的API更加人性化会话保持、代理设置、文件下载都非常方便。对于Wallheaven这种不需要执行JavaScript的静态内容获取requests完全够用。解析库json(内置) 和BeautifulSoup4由于核心数据来自API返回的JSONPython内置的json模块是首选速度快且无需额外依赖。但考虑到我们可能也需要从某些HTML页面如单个壁纸详情页中提取少量信息或者作为JSON接口的备用方案BeautifulSoup4这个HTML解析库也一并安装有备无患。并发处理concurrent.futures(内置)下载数百张图片如果使用单线程会耗费大量时间在等待网络I/O上。Python内置的concurrent.futures模块提供了线程池和进程池的高级接口我们可以用ThreadPoolExecutor来实现多线程并发下载极大提升效率。这里选择线程池而非进程池是因为下载任务主要是I/O密集型线程开销更小且能共享会话等资源。其他辅助库time/random用于在请求间插入随机延时模拟人类操作避免触发反爬。os/pathlib用于在本地创建分类文件夹、保存文件处理路径问题。pathlib是现代Python处理文件路径的推荐方式比传统的os.path更直观。logging用于记录程序运行日志包括成功下载、错误信息等方便后期排查问题。安装非内置库非常简单pip install requests beautifulsoup42.3 项目目录结构规划一个清晰的项目结构能让代码更易维护。我建议的目录结构如下wallheaven_crawler/ ├── main.py # 主程序入口 ├── config.py # 配置文件如API参数、下载路径、请求头等 ├── crawler.py # 核心爬虫类负责请求和解析 ├── downloader.py # 下载器类负责并发下载和文件保存 ├── utils.py # 工具函数如延时、日志初始化、链接构造 ├── logs/ # 日志文件目录 │ └── crawl_20231027.log └── wallpapers/ # 壁纸下载主目录可在config中配置 ├── landscape/ # 按分类存放如风景 ├── anime/ # 动漫 ├── general/ # 综合 └── ...这种模块化设计将不同职责分离比如crawler只关心如何获取数据downloader只关心如何高效下载和保存main.py负责协调它们工作。未来如果想增加对新网站的支持或者更换下载策略修改起来会非常方便。3. 核心代码实现与细节解析3.1 配置文件与常量定义首先我们把所有可配置的项集中到config.py中这比硬编码在代码里要灵活得多。# config.py import os from pathlib import Path # 基础下载路径 BASE_DOWNLOAD_PATH Path(./wallpapers) # API基础地址Wallhaven v1 API BASE_API_URL https://wallhaven.cc/api/v1/search # 单次请求获取的壁纸数量API的per_page参数最大为24 PER_PAGE 24 # 请求头用于伪装成普通浏览器 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Accept: application/json, text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Referer: https://wallhaven.cc/, # 添加Referer更真实 } # 网络请求配置 REQUEST_TIMEOUT 15 # 请求超时时间秒 MAX_RETRIES 3 # 失败重试次数 RETRY_DELAY 5 # 重试等待时间秒 # 爬取控制 # 请求间隔随机范围秒用于避免请求过快 DELAY_RANGE (1, 3) # 最大爬取页数防止意外设置过大 MAX_PAGES 50 # 日志配置 LOG_LEVEL INFO LOG_FILE Path(./logs/crawl.log)3.2 核心爬虫类实现crawler.py中的WallhavenCrawler类是大脑负责与网站API交互并提取壁纸信息。# crawler.py import requests import time import random import logging from typing import Dict, List, Optional from urllib.parse import urlencode from .config import HEADERS, REQUEST_TIMEOUT, MAX_RETRIES, RETRY_DELAY, DELAY_RANGE, BASE_API_URL, PER_PAGE class WallhavenCrawler: def __init__(self): self.session requests.Session() self.session.headers.update(HEADERS) self.logger logging.getLogger(__name__) def _make_request(self, url: str, params: Optional[Dict] None) - Optional[Dict]: 封装请求逻辑包含重试机制和延时 for attempt in range(MAX_RETRIES): try: # 在非首次尝试时延时 if attempt 0: time.sleep(RETRY_DELAY) self.logger.info(f第{attempt1}次重试请求: {url}) response self.session.get(url, paramsparams, timeoutREQUEST_TIMEOUT) response.raise_for_status() # 如果状态码不是200抛出HTTPError # 根据内容类型解析返回数据 content_type response.headers.get(content-type, ) if application/json in content_type: return response.json() else: # 如果不是JSON返回文本内容备用 self.logger.warning(f响应内容非JSON: {content_type}) return {html: response.text} except requests.exceptions.Timeout: self.logger.error(f请求超时: {url} (尝试 {attempt 1}/{MAX_RETRIES})) except requests.exceptions.HTTPError as e: self.logger.error(fHTTP错误: {e} (状态码: {response.status_code})) # 如果遇到403/429等可能被反爬延长等待时间 if response.status_code in [403, 429]: wait_time RETRY_DELAY * (attempt 2) self.logger.info(f遇到反爬状态码等待{wait_time}秒) time.sleep(wait_time) if response.status_code 404: self.logger.error(页面不存在停止重试。) break except requests.exceptions.RequestException as e: self.logger.error(f请求异常: {e} (尝试 {attempt 1}/{MAX_RETRIES})) # 每次请求后包括成功添加随机延时模拟人类浏览 time.sleep(random.uniform(*DELAY_RANGE)) self.logger.error(f请求失败已达最大重试次数: {url}) return None def search_wallpapers(self, query: str , categories: str 111, purity: str 100, sorting: str date_added, order: str desc, page: int 1) - Optional[List[Dict]]: 使用Wallhaven API搜索壁纸 :param query: 搜索关键词如“landscape” :param categories: 分类3位二进制字符串分别代表general/anime/people默认“111”全选 :param purity: 纯度3位二进制字符串分别代表sfw/sketchy/nsfw默认“100”仅sfw :param sorting: 排序方式relevance, random, date_added, views, favorites, toplist :param order: 排序顺序desc 或 asc :param page: 页码 :return: 壁纸信息列表失败返回None params { q: query, categories: categories, purity: purity, sorting: sorting, order: order, page: page, } # 过滤掉值为空的参数 params {k: v for k, v in params.items() if v} api_url BASE_API_URL self.logger.info(f正在搜索: {params}) data self._make_request(api_url, params) if data and data.get(data): wallpapers data[data] self.logger.info(f第{page}页获取到{len(wallpapers)}张壁纸信息。) # 提取我们需要的信息id, url, path, resolution, file_size simplified_list [] for wp in wallpapers: info { id: wp.get(id), url: wp.get(path), # 原图URL resolution: wp.get(resolution), file_size: wp.get(file_size), category: wp.get(category), } # 有时path字段可能为空我们可以用id构造一个不一定100%准确 if not info[url] and info[id]: # 构造规则https://w.wallhaven.cc/full/{id前两位}/{id}.{ext} # 但扩展名未知这里假设为jpg实际可能需要从thumbs字段推断 info[url] fhttps://w.wallhaven.cc/full/{info[id][0:2]}/wallhaven-{info[id]}.jpg simplified_list.append(info) return simplified_list else: self.logger.warning(f第{page}页未获取到数据或API返回异常。) return None这个类的关键点在于会话保持使用requests.Session()可以自动处理cookies并在多次请求间保持一些连接参数效率更高。健壮的请求封装_make_request方法内置了重试机制、异常处理和随机延时这是爬虫稳定性的基石。参数化搜索search_wallpapers方法暴露了API的主要参数你可以轻松地按关键词、分类、纯度、排序方式来筛选壁纸。例如想下载最新的风景壁纸可以设置querylandscape,sortingdate_added。3.3 并发下载器实现下载器downloader.py负责将爬虫获取到的图片链接列表高效、可靠地保存到本地。# downloader.py import requests import os from pathlib import Path import logging from concurrent.futures import ThreadPoolExecutor, as_completed from typing import List, Dict from .config import HEADERS, REQUEST_TIMEOUT, BASE_DOWNLOAD_PATH from .utils import sanitize_filename class WallpaperDownloader: def __init__(self, max_workers: int 5): :param max_workers: 并发下载的线程数不宜过高以免对目标服务器造成压力或被封 self.session requests.Session() self.session.headers.update(HEADERS) self.max_workers max_workers self.logger logging.getLogger(__name__) # 确保基础下载目录存在 BASE_DOWNLOAD_PATH.mkdir(parentsTrue, exist_okTrue) def _download_single(self, wallpaper_info: Dict, save_dir: Path) - bool: 下载单张壁纸 wp_id wallpaper_info.get(id, unknown) url wallpaper_info.get(url) if not url: self.logger.error(f壁纸 {wp_id} 无有效URL跳过。) return False # 从URL中提取文件名并做安全处理 filename url.split(/)[-1] safe_filename sanitize_filename(filename) # 也可以根据壁纸信息自定义文件名例如{id}_{resolution}.jpg # safe_filename f{wp_id}_{wallpaper_info.get(resolution, unknown)}.jpg file_path save_dir / safe_filename # 如果文件已存在跳过下载可根据需要修改为覆盖或重命名 if file_path.exists(): self.logger.info(f文件已存在跳过: {file_path}) return True try: response self.session.get(url, streamTrue, timeoutREQUEST_TIMEOUT) response.raise_for_status() # 检查Content-Type确认是图片 content_type response.headers.get(content-type, ) if image not in content_type: self.logger.warning(fURL返回非图片内容跳过: {url} (Content-Type: {content_type})) return False # 流式写入文件避免大文件占用过多内存 with open(file_path, wb) as f: for chunk in response.iter_content(chunk_size8192): if chunk: f.write(chunk) self.logger.info(f成功下载: {file_path} (大小: {file_path.stat().st_size / 1024:.2f} KB)) return True except requests.exceptions.RequestException as e: self.logger.error(f下载失败 [{wp_id}]: {url} - 错误: {e}) # 可选删除下载失败的空文件或部分文件 if file_path.exists(): file_path.unlink() return False def download_batch(self, wallpaper_list: List[Dict], category: str general) - Dict[str, int]: 批量下载壁纸 :param wallpaper_list: 壁纸信息字典列表 :param category: 分类名用于创建子文件夹 :return: 统计字典包含成功和失败数量 # 创建分类子目录 category_path BASE_DOWNLOAD_PATH / category category_path.mkdir(parentsTrue, exist_okTrue) stats {total: len(wallpaper_list), success: 0, failed: 0} if not wallpaper_list: self.logger.warning(壁纸列表为空无任务可下载。) return stats self.logger.info(f开始批量下载共{stats[total]}张壁纸保存至: {category_path}) # 使用线程池并发下载 with ThreadPoolExecutor(max_workersself.max_workers) as executor: # 提交所有下载任务 future_to_wp {executor.submit(self._download_single, wp, category_path): wp for wp in wallpaper_list} # 异步获取完成的任务结果 for future in as_completed(future_to_wp): wp_info future_to_wp[future] try: success future.result() if success: stats[success] 1 else: stats[failed] 1 except Exception as e: self.logger.error(f任务执行异常 [{wp_info.get(id)}]: {e}) stats[failed] 1 self.logger.info(f批量下载完成。成功: {stats[success]}, 失败: {stats[failed]}, 总计: {stats[total]}) return stats下载器的核心设计思想线程池并发ThreadPoolExecutor管理一个固定数量的线程池将下载任务分配给多个线程同时进行。对于I/O密集型的网络下载这能大幅缩短总耗时。max_workers设置为5是一个比较保守且友好的值既提升了速度又不会对目标服务器造成过大冲击。流式下载使用response.iter_content(chunk_size8192)以数据块的形式写入文件。这对于下载大尺寸的4K/8K壁纸至关重要可以避免将整个图片文件一次性加载到内存中。文件存在检查与安全命名下载前检查文件是否已存在避免重复下载。sanitize_filename是一个工具函数用于清理文件名中的非法字符如/,:,?等防止保存文件时出错。结果统计方法返回成功和失败的计数方便主程序汇总报告。3.4 工具函数与主程序协调utils.py和main.py将上述模块串联起来。# utils.py import re import logging import sys from pathlib import Path def sanitize_filename(filename: str) - str: 清理文件名中的非法字符 # 去除URL查询参数 filename filename.split(?)[0] # 替换Windows/Unix文件名中的非法字符为下划线 illegal_chars r[:/\\|?*] filename re.sub(illegal_chars, _, filename) # 限制文件名长度可选 if len(filename) 255: name, ext os.path.splitext(filename) filename name[:250] ext return filename def setup_logging(log_file: Path, level: str INFO): 配置日志系统 log_file.parent.mkdir(parentsTrue, exist_okTrue) logging.basicConfig( levelgetattr(logging, level.upper()), format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(log_file, encodingutf-8), logging.StreamHandler(sys.stdout) # 同时输出到控制台 ] )# main.py import logging import time from pathlib import Path from crawler import WallhavenCrawler from downloader import WallpaperDownloader from config import LOG_FILE, LOG_LEVEL, MAX_PAGES, BASE_DOWNLOAD_PATH from utils import setup_logging def main(): # 1. 初始化日志 setup_logging(LOG_FILE, LOG_LEVEL) logger logging.getLogger(__name__) logger.info(*50) logger.info(Wallhaven壁纸爬虫开始运行) logger.info(*50) # 2. 初始化爬虫和下载器 crawler WallhavenCrawler() downloader WallpaperDownloader(max_workers5) # 3. 定义搜索参数这里以“landscape”和“digital art”为例 search_queries [landscape, digital art] categories 110 # 只选择general和anime分类 purity 100 # 只下载SFW安全内容 sorting toplist # 按排行榜排序 order desc all_wallpapers [] for query in search_queries: logger.info(f\n开始爬取关键词: {query}) for page in range(1, MAX_PAGES 1): logger.info(f正在获取第 {page} 页...) wallpapers crawler.search_wallpapers( queryquery, categoriescategories, puritypurity, sortingsorting, orderorder, pagepage ) if not wallpapers: logger.warning(f第 {page} 页无数据可能已到末页停止爬取该关键词。) break all_wallpapers.extend(wallpapers) # 每爬取一页后可以稍作休息 time.sleep(2) logger.info(f关键词 {query} 爬取结束累计获取 {len([w for w in all_wallpapers if w.get(query)query])} 张壁纸信息。) # 4. 去重根据id unique_wallpapers [] seen_ids set() for wp in all_wallpapers: wp_id wp.get(id) if wp_id and wp_id not in seen_ids: seen_ids.add(wp_id) unique_wallpapers.append(wp) logger.info(f去重后总计获取 {len(unique_wallpapers)} 张唯一壁纸。) # 5. 批量下载 if unique_wallpapers: # 这里可以按分类下载示例中我们统一放到“mixed”文件夹或按查询词分类 # 按查询词创建子目录 for query in search_queries: # 简单过滤出属于当前query的壁纸注意实际API返回数据不包含query字段这里仅为示例逻辑 # 更合理的做法是根据爬取时的上下文来分类保存 pass # 简化处理全部下载到“downloads”目录 stats downloader.download_batch(unique_wallpapers, categorydownloads) logger.info(f最终下载统计: {stats}) else: logger.warning(未获取到任何壁纸信息下载任务终止。) logger.info(壁纸爬取任务全部完成。) if __name__ __main__: main()主程序的逻辑很清晰初始化 - 配置搜索任务 - 循环爬取多页 - 去重 - 并发下载。你可以通过修改search_queries等参数来定制你想要爬取的内容。4. 高级技巧与优化方案基础功能实现后我们可以从稳定性、效率和功能扩展上进行优化。4.1 应对反爬策略与提升稳定性Wallhaven虽然不算严苛但毫无节制的请求依然会被限制。动态User-Agent可以准备一个User-Agent列表每次请求随机选取一个模拟不同浏览器。# 在config.py中增加 USER_AGENTS [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..., Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 ..., # ... 更多UA ] # 在_make_request中随机设置 self.session.headers.update({User-Agent: random.choice(USER_AGENTS)})使用代理IP池如果IP被限制轮换代理是有效的解决方案。可以将代理IP列表放在配置文件中请求时随机选用。PROXIES [ {http: http://proxy1:port, https: https://proxy1:port}, # ... ] # 在请求时传入proxies参数 proxy random.choice(PROXIES) if PROXIES else None response self.session.get(url, proxiesproxy, ...)更智能的延时除了固定范围的随机延时还可以实现“自适应延时”即如果连续几次请求失败则自动延长等待时间如果请求顺利则逐渐缩短间隔。处理Cloudflare等防护如果网站启用了Cloudflare的5秒盾或验证码简单的requests将无法通过。此时需要考虑使用selenium、playwright等浏览器自动化工具来模拟真人操作或者寻找绕过的API但需注意法律和道德边界。对于Wallhaven目前API接口直接可用暂未遇到此问题。4.2 扩展功能元数据保存与分类管理仅仅下载图片文件是不够的。我们可能还想保存壁纸的元数据如ID、分辨率、标签、收藏数等方便日后检索。保存元数据到JSON或数据库在crawler.py的search_wallpapers方法中我们可以将完整的壁纸信息而不仅仅是下载链接保存下来。import json def save_metadata(wallpaper_list: List[Dict], filename: str metadata.json): with open(filename, w, encodingutf-8) as f: json.dump(wallpaper_list, f, ensure_asciiFalse, indent2)可以每爬取一页就追加保存或者全部爬完后统一保存。更精细的分类根据壁纸的categorygeneral, anime, people和puritysfw, sketchy字段自动创建不同的子文件夹存放让库更整洁。# 在downloader中根据壁纸信息决定保存路径 def get_save_path(self, wallpaper_info: Dict) - Path: category wallpaper_info.get(category, general) purity wallpaper_info.get(purity, sfw) # 例如wallpapers/general/sfw/ save_dir BASE_DOWNLOAD_PATH / category / purity save_dir.mkdir(parentsTrue, exist_okTrue) return save_dir根据分辨率筛选在爬取阶段就过滤掉不符合要求的壁纸。例如只下载宽度大于等于3840的4K壁纸。filtered_list [] for wp in wallpapers_from_api: res wp.get(resolution, 0x0) try: width, height map(int, res.split(x)) if width 3840: # 4K宽度 filtered_list.append(wp) except: pass4.3 性能优化与错误处理增强断点续传/状态保存爬取大量页面时程序可能因网络或异常中断。我们可以将已成功爬取的页码或壁纸ID记录到一个状态文件中下次启动时读取并跳过它们。STATE_FILE crawl_state.json def load_state(): try: with open(STATE_FILE, r) as f: return json.load(f) except FileNotFoundError: return {last_page: 0, downloaded_ids: []} def save_state(state): with open(STATE_FILE, w) as f: json.dump(state, f)更详细的错误日志与通知将错误分级严重的错误如连续请求失败可以通过邮件、钉钉机器人等方式通知开发者。可以使用logging.handlers.SMTPHandler或集成第三方库。控制并发与连接数过高的max_workers可能导致本地端口耗尽或被服务器封禁。通常5-10个线程是安全范围。对于下载还可以使用aiohttp实现异步IO性能更高但代码复杂度也增加。5. 常见问题与实战排坑记录在实际开发和运行过程中我遇到了不少典型问题这里记录下来供大家参考。5.1 请求被拒绝或返回空数据现象_make_request方法频繁返回None日志显示HTTP 403或429错误。排查检查请求头首先确认User-Agent、Referer等头部信息是否齐全且格式正确。直接用浏览器访问同一个API链接对比开发者工具中的请求头。检查频率立即停止程序大幅增加DELAY_RANGE例如调到5-10秒并加入随机延时。可能是请求太快触发了频率限制。检查IP状态用浏览器直接访问https://wallhaven.cc/看是否正常。如果不正常说明IP可能被临时封禁需要等待一段时间可能几小时或更换网络环境/使用代理。解决综合使用随机延时、更换User-Agent、使用代理IP池。对于Wallhaven保持礼貌的爬取间隔1-3秒/请求通常可以长期稳定运行。5.2 下载的文件损坏或不是图片现象下载的文件无法用图片查看器打开或者文件大小异常小几KB用文本编辑器打开发现是HTML错误页面。排查检查URL打印出失败的下载链接直接在浏览器中打开看是否重定向到了错误页面或验证页面。检查响应头在_download_single方法中务必检查Content-Type是否包含image。Wallhaven的图片CDN链接有时可能会因为防盗链或临时问题返回非图片内容。检查网络可能是网络波动导致下载不完整。实现重试机制并验证文件完整性例如对于JPEG可以尝试读取文件头。解决增加下载失败的重试逻辑对于返回非图片内容的URL将其加入黑名单或记录日志后跳过。5.3 文件名非法导致保存失败现象程序报错OSError: [Errno 22] Invalid argument特别是在Windows系统上。排查打印出准备保存的文件名看是否包含:、?、*等操作系统禁止在文件名中使用的字符。这些字符可能来自URL的查询参数部分。解决使用utils.sanitize_filename函数对文件名进行严格的清洗。切记不要只简单替换要确保替换后的文件名在目标操作系统上是合法的。5.4 内存占用过高或程序卡死现象爬取或下载大量图片时程序内存占用持续上升甚至卡死无响应。排查检查是否流式下载确认下载图片时使用了response.iter_content(chunk_size8192)而不是直接response.content。后者会将整个文件读入内存。检查线程池大小过高的max_workers如50以上会创建大量线程和网络连接可能导致资源竞争和系统负载过高。检查数据结构all_wallpapers列表是否在无限增长是否及时清理了不再需要的数据解决坚持使用流式下载将并发数控制在合理范围建议5-15对于海量爬取考虑将数据分批处理或直接存入数据库/文件而非全部放在内存列表中。5.5 如何爬取特定收藏夹或用户上传Wallhaven的API同样支持这些操作。关键在于找到正确的API端点。用户上传API格式为https://wallhaven.cc/api/v1/search?sortingdate_addedorderdesc但似乎没有直接按用户过滤的参数。可能需要结合搜索用户ID或从用户主页HTML解析这超出了基础API的范围。收藏夹收藏夹页面如https://wallhaven.cc/favorites的数据也是通过API加载的但需要用户登录后的Cookie才能访问私有收藏。请注意爬取需要认证的私有内容涉及账户安全和网站条款务必谨慎并确保你拥有相关权限。技术上你需要在requests.Session()中设置登录后获得的cookies。最后我想强调的是爬虫是一把双刃剑。在享受自动化便利的同时我们必须遵守目标网站的robots.txt规则尊重版权并将请求频率控制在合理范围内避免对他人服务造成影响。本项目中介绍的技术和思路主要用于学习和个人用途请勿用于大规模商业爬取或任何可能违反法律法规和网站条款的行为。希望这个详细的爬虫构建指南能帮助你顺利搭建自己的壁纸库。如果在实践过程中遇到新问题欢迎在评论区交流探讨。