尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用Python写一个自动下载并更换壁纸的脚本

用Python写一个自动下载并更换壁纸的脚本 你是不是也经常为了换一张壁纸翻半天图库我大概半年前开始每天早上打开电脑桌面上永远都是那张看了几个月的图终于某天受不了决定直接写个 Python 脚本让它每天自动去下载一批新壁纸顺便把桌面壁纸也给换了。说实话这个需求听起来很小但真正落地之后你每天打开电脑的心情完全不一样。这篇内容我会从需求分析、环境准备、数据来源、下载逻辑、自动化触发到中间踩过的坑完整讲一遍。适合有一定 Python 基础、想练手写爬虫脚本或者纯粹想让壁纸自己长出来的朋友。全程使用免费且合法的壁纸来源不涉及任何破解、付费绕过或者灰产玩法代码逻辑也足够简单清晰你可以直接照着抄也可以在此基础上扩展成自己的图库同步工具。1. 写这个脚本之前先把需求想透壁纸脚本到底要解决什么问题很多教程上来就贴代码但真正动手写过自动下载脚本的人都知道如果需求没想清楚代码改起来会很痛苦。我最初的目标特别朴素每天自动从网上拿一张我喜欢风格的壁纸存到本地指定目录然后如果能顺手替换桌面壁纸就更好了。但拿一张壁纸这个动作背后牵扯出来的问题其实不少。1.1 我为什么突然想写这个脚本以前我是手动逛壁纸网站看到喜欢的图右键另存为然后右键设置桌面背景。这流程偶尔一次还行但坚持不下来。因为人每天对壁纸的新鲜感阈值是越来越高的今天你可能觉得这图好看明天就想换可你不可能每天花半小时去翻图库。后来我尝试过一些壁纸软件要么广告多要么内置了很多你用不上的功能要么就是图片质量参差不齐还需要注册账号。我本身是做脚本和自动化相关工作的这种场景最适合写个小工具来解决于是就有了这个项目。它本质上就是一个采集 落地 自动执行的小管道核心逻辑放在任何数据采集任务里都通用所以学到的技能也不只是换壁纸这一件事。1.2 三种常见壁纸获取方式我最终怎么取舍动手之前我列了一下获取壁纸数据的常见方式手动下载最简单但完全谈不上自动排除。调用第三方壁纸 API一键返回 JSON里面是图片 URL、作者、分辨率等信息解析起来最舒服。缺点是免费 API 要么图片风格单一要么有调用频次限制还有的可能需要注册 token。自己写爬虫解析壁纸网站可选范围广几乎任何站点都能适配但需要处理 HTML 结构变化、反爬机制、分页逻辑这些问题工作量相对大。最终我选择的是API 为主、网站解析为辅的组合方案。先用免费 API 保证脚本能跑通再把站点解析部分做成可插拔的模块后面想换图片来源只需要改一个函数就行。这套思路也推荐给你一开始不要追求一把梭写全所有来源先跑通主线再优雅扩展。1.3 确认边界数量、清晰度、权限与版权还有一个很容易被忽略的点你下载壁纸用于个人桌面和把壁纸图库二次分发是完全不同的法律性质。个人使用场景下大多数免费壁纸站是允许下载的但注意看清楚站点的服务条款尽量选那些明确标注 Free to use 或者基于 CC0 协议的图片。除了版权还要提前确定好两个边界数量控制。不要一次性拉全站图片脚本需要设置单次下载上限比如每次最多下载 5 张避免给目标站点造成压力也避免把磁盘塞满。清晰度过滤。同一个壁纸 URL 可能对应多种分辨率脚本里必须有过滤逻辑低于 1920x1080 的直接跳过否则辛辛苦苦下载回来结果设成桌面全是马赛克。需求理清楚之后后面每一步都有明确目标了。现在说说环境怎么配。2. 环境准备与请求基础先把 Python 运行环境弄利索写爬虫脚本运行环境是第一个门槛。很多新手在环境配置上栽跟头不是 Python 没装好就是第三方库装不上再就是执行时编码出错。我把最省心的配置路径完整写一遍照着走基本不会出问题。2.1 从零配置 Python 环境与依赖库我假设你电脑上目前没有任何 Python 环境。如果你已经装好了可以直接跳过安装部分。首先去 Python 官网下载对应系统的安装包我目前用的是 Python 3.10 以上的版本3.8 也没有问题。安装的时候有一个非常关键的勾选Add Python to PATH这个必须勾上不然后续在命令行里输入 python 会提示无法识别。装好后打开终端Windows 上是 PowerShell 或 CMDmacOS/Linux 上是 Terminal输入python --version如果输出类似Python 3.10.x说明环境变量没问题。然后我们需要安装三个库requests发 HTTP 请求下载图片和解析接口数据。Pillow验证图片完整性检查尺寸防止下载到损坏文件。schedule用于脚本内部的定时任务如果你不依赖系统任务计划可以用它。安装命令是pip install requests Pillow schedule如果你使用的是 macOS 或者 Linux命令行里可能需要用pip3代替pip这个看系统配置。安装完可以用pip list确认三个库都在。2.2 先用一个简单请求验证网络连通性很多人一上来就写完整脚本然后运行报错根本分不清是代码问题还是网络问题。我的习惯是先用三行代码做连通性测试import requests url https://picsum.photos/1920/1080 resp requests.get(url, timeout10) print(resp.status_code, len(resp.content))如果输出200并且后面的数字大于几万说明网络和 requests 库的安装都没问题。如果超时或者报SSLError大概率需要检查代理设置或者本机防火墙。这里有个小经验requests 的timeout参数一定要写不写的话一旦目标站点不响应你的脚本会一直挂在那里。2.3 解析壁纸数据JSON 比 HTML 好洗刚开始写爬虫的人会把重心放在怎么解析 HTML上但我更建议先学会解析 JSON。原因很简单JSON 是结构化数据里面是清晰的键值对而 HTML 需要借助 BeautifulSoup 或者正则表达式去提取页面结构一改脚本就废。比如很多免费壁纸 API 会返回类似这样的数据{ id: 123, author: John, download_url: https://images.example.com/123/1920/1080, width: 1920, height: 1080 }拿到这个数据你只需要resp.json()转成字典然后取download_url字段就行。解析 HTML 则是完全另一套思路要定位标签、class、属性写起来明显琐碎。所以我的建议很直接能走 API 绝不去爬 HTML必须爬 HTML 时才动用解析库。3. 两种图片来源实现从公开 API 到网页解析现在我们进入脚本的核心部分数据来源。我打算给你展示两套实现第一套是使用公开 API第二套是解析网页。两套代码都不长你根据自己的偏好选择。3.1 方案 A调用公开壁纸 API直接拿高清图这里用picsum.photos作为示例它是一个非常经典的免费占位图服务图片来自 Unsplash接口简单不需要注册。import requests def fetch_picsum_url(target_width1920, target_height1080): api_url fhttps://picsum.photos/{target_width}/{target_height} # 不直接下载图片而是跟随重定向拿到真实图片地址 resp requests.head(api_url, allow_redirectsTrue, timeout10) return resp.url这段逻辑是我们请求https://picsum.photos/1920/1080服务端会返回一张符合尺寸的图片但直接下载它也行。这里用requests.head拿到重定向之后的真实 URL好处是可以在下载前打日志方便排查。如果你想获取多张图片的一次性列表可以使用https://picsum.photos/v2/list?page1limit5这个接口它会返回一个包含多张图片信息的 JSON 数组。代码差不多是这样def fetch_picsum_list(page1, limit5): api_url fhttps://picsum.photos/v2/list?page{page}limit{limit} resp requests.get(api_url, timeout10) resp.raise_for_status() return resp.json()有了列表之后遍历每一条数据取出download_url和width、height就能做格式校验和下载了。API 方案的好处就是稳基本不需要维护适合脚本跑在服务器或开发机上长期定时执行。3.2 方案 B解析壁纸站点列表页拿到更多风格如果你觉得内置 API 的图风格不够多样那就自己从壁纸站解析。不过需要注意解析第三方站点时要遵守 robots 协议不要高频请求。我这里用一个常见的列表页结构做演示假设页面上每个壁纸卡片都是一个a标签里面包含href和>import requests from bs4 import BeautifulSoup def fetch_wallpaper_urls_from_html(): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } url https://example-wallpaper-site.com/toplist resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() soup BeautifulSoup(resp.text, html.parser) items soup.select(a.wallpaper-item) result [] for item in items: img_url item.get(data-res) if img_url: result.append(img_url) return result注意为了方便演示我用的是伪代码真实站点的选择器和属性名需要你根据目标站点的 HTML 结构自行调整。解析 HTML 的核心思路是先用soup.select定位所有包含图片信息的容器再提取其中的属性或子节点文本。BeautifulSoup 的select使用的是 CSS 选择器语法会一点前端知识会大大提速。3.3 为什么要单独加一层去重 随机逻辑不管用哪种来源下载脚本都必须面对同一个问题重复。壁纸 API 的分页虽然会变但某些图有可能反复出现在不同页里网页解析也有类似情况。所以我在下载模块里加了一层历史记录去重。最简单的去重做法是维护一个本地downloaded.txt文件每下载一张图就把它的 URL 写进去。下次遇到相同 URL 直接跳过。代码实现import os HISTORY_FILE downloaded.txt def load_history(): if not os.path.exists(HISTORY_FILE): return set() with open(HISTORY_FILE, r, encodingutf-8) as fp: return set(line.strip() for line in fp if line.strip()) def save_history(url): with open(HISTORY_FILE, a, encodingutf-8) as fp: fp.write(url \n)随机逻辑就更有意思了。如果每次都从第一张开始下载那你的壁纸永远是按列表顺序来的可能连续几天都是同一个作者的作品。我推荐把所有符合条件的图片 URL 放进一个候选池然后随机抽取指定数量。import random def pick_random_images(url_list, count3): return random.sample(url_list, min(count, len(url_list)))这两层逻辑加进去之后你每天拿到的壁纸既不会重复又足够多样。很多人写脚本只关注能不能下载忽略了下载的是否合理结果积累了半年全是类似的图这正是算法设计上的小细节。4. 下载与落地文件命名、格式校验与增量更新数据来源解决之后下一步就是把图片从网络流落到本地磁盘。这个环节看着简单但坑也不少。如果你直接使用resp.content一次性读入内存大图片会非常吃内存尤其是动辄几十 MB 的高清壁纸。所以我推荐使用流式下载。4.1 流式下载内存友好的图片写入方式流式下载的思路是不一次性把图片内容全部加载到内存而是分成小块写入磁盘def download_image(img_url, save_path): headers {User-Agent: Mozilla/5.0} with requests.get(img_url, headersheaders, streamTrue, timeout30) as resp: resp.raise_for_status() with open(save_path, wb) as fp: for chunk in resp.iter_content(chunk_size8192): if chunk: fp.write(chunk)这里的iter_content(chunk_size8192)表示每 8KB 写一次磁盘。好处是即使图片有 30MB内存占用也始终保持在可控范围。还有一点使用with语句打开响应请求结束后资源会自动释放不会出现连接泄漏。4.2 防止重复下载用文件命名和哈希双重判断除了 URL 去重我还做了第二层去重文件层面判断。因为同一个 URL 如果重定向内容变了URL 历史记录就失效了。但在同一台设备上如果本地已经存在目标文件名的文件我们也应该跳过。图片文件名最好能体现图片的特征。我用的命名规则是{日期}_{图片ID}.jpg。日期代表下载时间图片 ID 从 URL 里提取这样一眼就能看出来源。如果还需要更严格的去重可以在写出文件后计算文件的 MD5 值和已知列表比对import hashlib def md5_of_file(filepath): hash_md5 hashlib.md5() with open(filepath, rb) as fp: for chunk in iter(lambda: fp.read(4096), b): hash_md5.update(chunk) return hash_md5.hexdigest()MD5 去重的代价是每次都要读一遍文件壁纸数量少的时候没有问题几千张图片的话性能也还能接受。日常使用里文件名加 URL 双重去重已经足够。4.3 清晰度过滤与损坏文件清理下载完成后必须用 Pillow 验证图片是否完整。有时候网络闪断会导致下载进度不完整文件后缀是.jpg但实际上无法打开。Pillow 打开它时就会抛异常我们可以利用这个特征做清理from PIL import Image def validate_image(filepath, min_width1920, min_height1080): try: with Image.open(filepath) as im: width, height im.size im.verify() except Exception: os.remove(filepath) return False if width min_width or height min_height: os.remove(filepath) return False return Trueim.verify()会检查文件结构是否完整但不能替代im.size的读取。所以我的顺序是先 verify再取尺寸如果不满足最小分辨率就直接删除保证进入壁纸目录的每一张图都能正常使用。4.4 最终下载主流程串起来有了上面的函数主流程就是def download_batch(img_urls, download_dir): os.makedirs(download_dir, exist_okTrue) history load_history() saved 0 for url in img_urls: if url in history: continue file_id url.split(/)[-1].split(?)[0] save_path os.path.join(download_dir, f{file_id}.jpg) try: download_image(url, save_path) except Exception as e: print(f下载失败: {url}, 错误: {e}) continue if validate_image(save_path): save_history(url) saved 1 if saved 3: break return saved这段代码里的saved 3是单次任务上限防止机器请求频率过高。你可以按需调整但不建议一次性下载太多。5. 自动化触发怎么让脚本真正自动跑起来脚本写完只完成了一半剩下的一半在于自动。让脚本定时执行最主流的方式是利用操作系统自带的任务计划这样即使 Python 进程崩溃退出系统也会在下一个时间点重新拉起来。5.1 Windows 任务计划程序配置完整步骤Windows 系统推荐使用任务计划程序。按Win R输入taskschd.msc回车打开任务计划程序。在右侧点击创建基本任务。名称填写Auto Wallpaper Download。触发时间可以选择每天设定一个你通常开电脑的时间比如早上 8 点。操作选择启动程序程序填写python的完整路径参数填写你的脚本路径比如D:\scripts\wallpaper_downloader.py。点击完成。有个细节不要直接在程序或脚本里填 python然后参数里填绝对路径。很多时候任务计划执行失败是因为系统找不到 python 命令。用where python查到完整路径填进去比如C:\Python310\python.exe能避免绝大多数问题。5.2 macOS 和 Linux 系统下的 cron 写法macOS 和 Linux 用户可以用 crontab。crontab -e然后在文件末尾追加一行0 8 * * * /usr/bin/python3 /home/yourname/wallpaper_downloader.py这里的0 8 * * *代表每天早上 8 点执行。分三个部分理解分钟、小时、日期、月份、星期。0 8 * * *就是每天 8 点 0 分。macOS 用户注意新版本系统出于省电和隐私考虑可能不会准时唤醒执行 cron 任务。如果你的 Mac 经常合盖建议在系统设置 - 电池 - 选项里开启定时启动或唤醒或者改用launchd配置但那套配置更复杂绝大多数人用 cron 就够了。5.3 脚本自身的异常重试与日志系统级定时任务能解决能不能跑但解决不了跑挂了没人知道。为了让脚本更健壮我给它加了三层保险第一层单张图片下载加异常捕获。下载失败不要影响整批任务的执行跳过这张继续下一张。第二层全流程包一层 try-except。一旦有意外异常写日志文件而不是窗口打印因为定时任务执行时你是看不到窗口输出的。import logging logging.basicConfig( filenamewallpaper_downloader.log, levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s )第三层设置最大重试次数。对失败的任务重试三次每次间隔 5 秒用来应对临时网络抖动。def download_with_retry(url, save_path, retries3): for attempt in range(retries): try: download_image(url, save_path) return True except Exception as e: logging.warning(f第 {attempt 1} 次下载失败: {e}) time.sleep(5) return False有了这三层我才敢放心让脚本无人值守跑着。5.4 用 schedule 库实现进程内定时除了依赖系统任务计划你也可以在脚本内部使用schedule库实现定时适合那些希望脚本常驻后台且不想碰系统配置的场景。import schedule import time def job(): print(开始执行壁纸下载任务...) main() schedule.every().day.at(08:00).do(job) while True: schedule.run_pending() time.sleep(60)不过这只适合你一直开着电脑的情况笔记本合盖睡眠后定时任务会暂停。如果要稳定运行我还是建议用系统自带的任务计划配合脚本内的重试逻辑双保险。6. 实测中踩过的坑与效果复盘最后聊几个我实际跑这个脚本时踩过的坑每一个都是真实案例比看十遍文档都管用。6.1 坑一请求头缺失导致 403第一次跑脚本我直接用requests.get(url)去请求壁纸站结果返回 403 Forbidden。这个状态码的意思是服务器拒绝了你的请求。原因很简单很多站点会检查请求头里的User-Agent如果没有或者太低级会被当作爬虫拦截。解决办法是伪装成一个正常的浏览器请求headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0 Safari/537.36, Referer: https://example-wallpaper-site.com }加完之后大部分 403 都能解决。如果还不行可能需要加 Cookie。但这涉及模拟登录复杂度就上去了个人壁纸脚本不建议在这个方向死磕换一个允许公开访问的来源更划算。6.2 坑二下载到一半的损坏图片刚开始我下载完图片没有校验直接用open设置桌面壁纸。有一次放完假回来看桌面变成了一片灰色后来排查发现是网络中断导致某张图片只下载了 30% 的字节文件本身已经损坏。这种情况要靠 Pillow 校验来兜底如前面那节写的validate_image函数。每次下载完立刻验证无效文件当场删掉。这一步不能省否则你自动化时间长了壁纸目录里会攒下一堆打不开的坏文件。6.3 坑三爬虫请求太猛IP 被临时封禁还有一次我为了测试批量下载逻辑把单次任务上限调到了 50结果几分钟内请求频率过高网站直接把我的 IP 临时封了所有请求都超时或返回 403。解封之后我很长时间都把单次下载上限控制在 5 张以内并且每次请求之间至少间隔 1 秒import time for url in url_list: download_image(url, save_path) time.sleep(1)睡眠 1 秒看着不多但能极大降低被封风险。凡是做爬虫相关脚本一定要有礼貌请求的意识这不仅是对目标站点的尊重也是保证自己 IP 安全的基础。6.4 最终效果与日常使用体验目前我的壁纸脚本已经在树莓派上连续运行了几个月每天早上下载 3 张新壁纸存到~/Wallpapers目录然后通过一个简单的系统触发器设置桌面背景。由于在下载前做了分辨率过滤实际设置之后几乎没有出现过模糊拉伸的情况。壁纸来源我用的是 API 为主偶尔手动添加一些主题站点的解析规则周末换换口味。这个脚本真正让我满意的不是换壁纸这个结果而是养成了一种思维方式凡是我每周都要重复做两次以上的数字琐事都值得写个脚本跑起来。壁纸只是第一个项目后续我还打算在这个框架上增加按主题分类、自动上传到云相册、甚至做成一个简单的壁纸聚合站。你的第一个自动化脚本也可以从桌面壁纸开始因为它足够简单反馈足够快踩坑成本也低。真跑起来之后你就知道写脚本解决自己的小烦恼这件事有多上瘾了。
返回列表