
图片爬虫最烦的不是把图片下载下来。
真正麻烦的是:跑半天存了一堆缩略图、重复图、404,最后目录里还混着几个几十 KB 的小图。
所以我写图片下载脚本时,一般不直接对网页里的 <img> 标签开抓。能走公开 API 就走 API,数据干净,图片尺寸、文件类型这些信息也能直接拿到。
这次抓点我自己平时会存的壁纸:汽车、机械、赛博朋克。
直接上 Python。
先装一个依赖:
pip install requests
Wallhaven 有公开的搜索 API,普通 SFW 图片不需要 API Key,搜索接口能直接返回图片地址、分辨率、文件大小等信息。
我先写个最小版本试接口:
import requests
api = "https://wallhaven.cc/api/v1/search"
params = {
"q": "sports car",
"purity": "100",
"sorting": "favorites",
"atleast": "1920x1080"
}
resp = requests.get(api, params=params, timeout=10)
resp.raise_for_status()
for item in resp.json()["data"][:5]:
print(item["resolution"], item["path"])
第一次写爬虫,我一般先停在这里。
别一上来就 while 循环跑几十页。先确认接口返回的数据是不是自己想要的,不然脚本吭哧吭哧下载半小时,最后发现拿到的全是小图,那就挺冤。
接口通了,再开始落盘。
我把下载部分单独拆出来:
from pathlib import Path
import hashlib
import requests
SAVE_DIR = Path("wallpaper_box")
SAVE_DIR.mkdir(exist_ok=True)
session = requests.Session()
session.headers.update({
"User-Agent": "WallpaperCollector/1.0"
})
def save_picture(url, tag):
r = session.get(url, timeout=20)
r.raise_for_status()
body = r.content
# 太小的文件我直接不要
if len(body) < 200 * 1024:
print("skip small:", url)
return
digest = hashlib.md5(body).hexdigest()
suffix = ".png" if "png" in r.headers.get("Content-Type", "") else ".jpg"
file_path = SAVE_DIR / f"{tag}_{digest[:12]}{suffix}"
if file_path.exists():
print("skip repeat:", file_path.name)
return
file_path.write_bytes(body)
print("saved:", file_path.name)
这里我没有直接拿图片原文件名保存。
图片站上的名字经常没什么意义,而且多关键词抓取以后可能重复。直接对内容算一个 MD5,既能当文件名,也顺手解决一部分重复下载的问题。
接下来把搜索和分页补上。
Wallhaven 的搜索结果是分页返回的,每页最多 24 条,所以分页别自己瞎猜,直接读返回结果里的 meta。
import time
SEARCH_API = "https://wallhaven.cc/api/v1/search"
def collect(keyword, pages=2):
for page in range(1, pages + 1):
params = {
"q": keyword,
"categories": "100",
"purity": "100",
"sorting": "favorites",
"order": "desc",
"atleast": "1920x1080",
"page": page
}
try:
r = session.get(SEARCH_API, params=params, timeout=12)
r.raise_for_status()
payload = r.json()
except requests.RequestException as exc:
print(f"[{keyword}] page={page} request failed: {exc}")
continue
pictures = payload.get("data", [])
print(f"[{keyword}] page={page}, found={len(pictures)}")
for pic in pictures:
width = pic.get("dimension_x", 0)
height = pic.get("dimension_y", 0)
if width < 1920 or height < 1080:
continue
try:
save_picture(pic["path"], keyword.replace(" ", "_"))
except requests.RequestException as exc:
print("download failed:", exc)
time.sleep(0.4)
time.sleep(1)
这里两个 sleep 我一般会留着。
爬虫不是越快越牛。尤其是这种公共服务,你本地 20 个线程一起狠狠干,对自己没什么收益,对别人服务器倒挺有压力。
最后把关键词放进去:
topics = [
"sports car",
"motorcycle",
"cyberpunk city",
"mechanical keyboard"
]
for topic in topics:
collect(topic, pages=2)
跑起来以后目录大概就是这种:
wallpaper_box/
├── sports_car_18bb42aa130e.jpg
├── sports_car_a28d7195be31.jpg
├── motorcycle_2fbe77de0df1.jpg
├── cyberpunk_city_692ac6af20e1.jpg
└── mechanical_keyboard_a122f91bce20.jpg
图片爬虫做到这里其实已经够用了。
我反而不建议急着上多线程。
图片下载属于 IO 操作,并发确实能提速,但只要线程数一开大,429、超时、连接重置这些东西马上就来了。最后代码里全是重试和限速,原本几十行能解决的事情,硬生生写成一个下载框架。
还有一个地方得注意。
这里我把 purity 固定成了 100,也就是只搜 SFW 内容。官方文档里 NSFW 内容需要有效 API Key,我这个脚本压根没必要碰。
真拿这个脚本自己玩,关键词比代码重要。
比如:
sports car
racing
motorcycle
space
cyberpunk city
mechanical keyboard
coding setup
samurai
mountains
aircraft
换一批关键词,就是另一套图库。
后面如果还想继续折腾,可以再加一个 Pillow,对下载后的图片做二次筛选,比如只保留横屏图:
from PIL import Image
def is_desktop_wallpaper(path):
with Image.open(path) as img:
w, h = img.size
return w / h >= 1.6 and w >= 1920
这种过滤我觉得比“再多爬一万张”有用。
图片脚本最后拼的不是下载速度,而是留下来的东西干不干净。
请求接口、筛尺寸、做去重、控制频率、失败继续跑,这几个地方处理好,一个几十行的 Python 脚本,平时给电脑换壁纸已经完全够了。
另外,图片能下载不等于可以随便商用。自己学习、整理壁纸和拿图片做网站、公众号配图是两回事,后者最好再确认原图版权和授权信息。Wallhaven API 的搜索、分页和 SFW 过滤规则以官方文档为准。
以上就是“Python 爬虫:批量爬点男生喜欢的高清图片,顺手把几个坑踩一遍”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://www.phpxs.com/post/14524/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料