Python 爬虫实战:单页图片批量下载与分类本地文件夹存储
前言在爬虫落地应用场景中图片资源批量抓取是高频开发需求各类素材站点、资讯站点、图库网站均以 HTML 内嵌图片链接、CDN 资源地址的形式存放图片数据原生手动保存单张图片效率低下依托 Python 爬虫自动化实现批量抓取、目录分级存储、格式校验是工程化数据归集的常规方案。本章节依托 requests 完成网络二进制流拉取、os 模块完成本地目录自动化创建、pathlib 实现跨系统路径兼容从路径设计、链接提取、流写入、异常容错四个维度落地图片爬虫配套分级分类存储逻辑解决多规格图片混存、目录缺失报错、下载中断损坏文件等实操痛点。本文涉及核心开发资源超链接如下requests 官方开发文档HTTP 二进制资源请求依赖库负责图片字节流获取Python os 模块官方文档操作系统目录、文件操作内置库自动化创建分类文件夹Python pathlib 标准库文档面向对象路径处理兼容 Windows、Linux、macOS 路径分隔符差异PIL (Pillow) 官方文档图片格式校验依赖库筛选损坏图片、区分 jpg/png/webp 等后缀格式。一、图片下载爬虫前置理论知识1.1 网页图片资源存储原理网页前端渲染的图片资源主要分为两类部署形式其一为相对路径资源以/static/img/xxx.jpg、./img/xxx.png格式挂载站点服务器其二为绝对 CDN 全链接资源以https://xxx.cdn.com/file/xxx.webp完整 URL 格式存在爬虫仅能通过完整网络地址发起请求获取图片二进制数据。浏览器访问图片地址时服务器返回图片二进制字节流客户端通过解码渲染可视化图片爬虫的核心逻辑即直接接收二进制数据流以字节模式写入本地磁盘文件复刻图片源文件。服务器针对图片资源存在差异化响应策略部分图库站点会对图片接口设置 Referer 来源校验、UA 校验缺失合法请求头直接返回空白二进制数据这也是前文 UA 伪装知识点在图片爬虫场景的落地应用场景。1.2 本地分类存储目录设计规范规范的目录架构是实现图片分类管理的前提行业通用分级目录结构采用「根目录 - 分类子目录 - 日期归档子目录」三级架构目录结构示例plaintextimage_spider/ ├─ nature/ # 自然风光分类文件夹 │ ├─ 20260606/ │ ├─ img_001.jpg │ ├─ img_002.png ├─ animal/ # 动物素材分类文件夹 │ ├─ 20260606/ │ ├─ img_001.webp该架构优势体现在三方面第一按业务类目拆分文件夹实现素材精准分类第二按抓取日期创建次级目录避免单日大批量图片文件名重复覆盖第三层级固定便于后期遍历、批量迁移、筛选失效图片。1.3 核心依赖库功能梳理表格依赖库名称安装指令核心业务作用requestspip install requests发起图片 URL 请求获取 Response.content 二进制字节数据Pillowpip install pillow校验下载后图片完整性剔除损坏无法打开的异常文件os内置无需安装兼容低版本 Python递归创建多级文件夹判断目录是否存在pathlibPython3.4 内置跨平台路径拼接规避 Windows 反斜杠 \ 与 Linux 斜杠 / 路径语法冲突二、基础版单页图片批量下载实现单目录存储2.1 实现思路拆解配置目标图片所在页面 URL 与自定义存储文件夹名称调用 requests 携带伪装请求头请求网页源码规避基础反爬拦截通过正则表达式匹配页面内所有图片绝对 URL 链接自动判断目标存储文件夹是否存在不存在则调用 os.mkdir 创建循环遍历图片链接逐个请求获取二进制数据以追加字节方式写入本地增加单次下载异常捕获单张图片下载失败跳过不中断整体爬虫程序。2.2 完整基础代码示例python运行import requests import os import re # 全局配置项 BASE_SAVE_DIR spider_image # 基础存储根目录 TARGET_URL 测试素材页面地址 # 替换为实际含图片的目标网页地址 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9 } def create_folder(folder_path): 文件夹创建函数目录不存在则新建 if not os.path.exists(folder_path): os.mkdir(folder_path) print(f成功创建存储目录{folder_path}) def get_all_img_url(html_text): 正则匹配页面全部图片链接 # 正则匹配jpg、png、webp三种主流图片格式的http/https全链接 img_pattern re.compile(rhttps?://[^\s]?\.(?:jpg|png|webp|jpeg)) url_list img_pattern.findall(html_text) # 列表去重剔除同一图片重复抓取 unique_url list(set(url_list)) return unique_url def download_single_img(img_url, save_path): 单张图片下载函数二进制写入本地 try: resp requests.get(img_url, headersHEADERS, timeout15) # 状态码200代表资源正常返回 if resp.status_code 200: with open(save_path, wb) as f: f.write(resp.content) print(f下载完成{os.path.basename(save_path)}) return True else: print(f资源链接失效状态码{resp.status_code}{img_url}) return False except Exception as err: print(f下载异常 {img_url} 错误信息{str(err)}) return False if __name__ __main__: # 初始化存储目录 create_folder(BASE_SAVE_DIR) # 请求页面源码 page_resp requests.get(TARGET_URL, headersHEADERS, timeout10) page_resp.encoding page_resp.apparent_encoding html page_resp.text # 提取全部图片地址 img_urls get_all_img_url(html) print(f页面共匹配到去重后图片数量{len(img_urls)}) # 循环批量下载 for index, url in enumerate(img_urls): # 截取图片原始后缀拼接本地保存文件名 suffix url.split(.)[-1] save_file_name fimg_{index1}.{suffix} full_save_path os.path.join(BASE_SAVE_DIR, save_file_name) download_single_img(url, full_save_path)2.3 代码逐段原理解析全局配置区统一管理存储目录、请求头、目标网址项目迭代时仅需修改配置参数无需改动业务逻辑代码HEADERS 复用前文 UA 伪装逻辑规避站点通过 UA 拦截图片资源请求。create_folder 函数依托 os.path.exists 判断路径物理存在性os.mkdir 完成单层目录创建避免目标文件夹缺失触发文件写入 IOError 报错。get_all_img_url 函数正则表达式限定 http/https 协议 主流图片后缀精准筛选图片资源链接set 去重消除页面中同一张图片多次引用带来的重复下载损耗降低服务器请求压力与本地冗余文件占用。download_single_img 核心下载函数resp.content属性直接获取接口返回的原始二进制字节文件打开模式设置为wb二进制覆写模式区别于文本爬虫w字符写入模式二进制模式是图片、视频、压缩包等非文本资源落地磁盘的核心语法try-except 捕获网络超时、链接失效、磁盘空间不足等异常单文件报错不终止整体循环。主程序执行逻辑先拉取整页 HTML 源码解析图片链接集合通过索引自增命名文件利用 os.path.join 实现跨系统路径拼接自动适配 Windows 与 Linux 路径规则。2.4 基础版方案优缺点表格优势短板代码结构精简入门调试成本低快速实现单页全图下载所有图片统一存入单一文件夹多类目素材混杂无法分类依赖少仅 requests 为第三方库部署便捷文件命名依靠自增序号无法保留原文件名溯源困难自带链接去重、异常捕获基础容错能力完善缺少图片完整性校验存在下载半截损坏图片占用存储空间三、进阶版分类存储图片爬虫多级目录 素材分类3.1 优化升级点说明针对基础版存储混乱问题进阶方案引入类目参数 日期子目录 原文件名保留 图片完整性校验四项优化采用 pathlib 重构路径代码实现全平台兼容引入 Pillow 校验图片字节有效性新增自定义分类入参支持爬取不同栏目图片自动归类至对应文件夹。3.2 进阶完整代码实现python运行import requests import re import os from pathlib import Path from PIL import Image from datetime import datetime # 全局配置 ROOT_DIR Path(./image_store) # 当前日期字符串格式YYYYMMDD用作次级归档目录 CURR_DATE datetime.now().strftime(%Y%m%d) HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.baidu.com } def auto_mk_multi_dir(class_name): 创建三级目录根目录/分类名/日期目录 target_dir ROOT_DIR / class_name / CURR_DATE # mkdir参数parentsTrue递归创建多级目录exist_okTrue目录存在不报错 target_dir.mkdir(parentsTrue, exist_okTrue) return target_dir def filter_valid_img(file_path): Pillow校验图片完整性损坏文件直接删除 try: with Image.open(file_path) as img: img.verify() return True except Exception: os.remove(file_path) print(f损坏图片已自动删除{file_path.name}) return False def parse_img_links(html): reg re.compile(rhttps?://.?\.(jpg|jpeg|png|webp)) link_list list(set(reg.findall(html))) return link_list def download_with_class(img_url, save_dir): # 提取原始文件名 origin_name img_url.split(/)[-1] save_full_path save_dir / origin_name try: resp requests.get(img_url, headersHEADERS, streamTrue, timeout20) if resp.status_code ! 200: return False # 流式写入大体积图片减少内存占用 with open(save_full_path, wb) as f: for chunk in resp.iter_content(chunk_size1024*8): if chunk: f.write(chunk) # 下载完成校验图片有效性 filter_valid_img(save_full_path) print(f已保存{origin_name}) return True except Exception as e: print(f下载失败{img_url}:{str(e)}) return False if __name__ __main__: # 可自定义当前爬取素材分类名称 classify_name animal_pic page_url 目标素材页面URL # 生成对应分类目录 save_folder auto_mk_multi_dir(classify_name) # 获取页面源码 res requests.get(page_url, headersHEADERS) html_data res.text img_links parse_img_links(html_data) print(f本次待下载图片总数{len(img_links)}) for link in img_links: download_with_class(link, save_folder)3.3 进阶优化核心原理剖析pathlib 路径管理Path 对象使用/运算符拼接路径自动适配不同操作系统路径分隔符mkdir(parentsTrue,exist_okTrue)一行代码递归创建多级目录替代 os 多级目录循环判断代码代码可读性大幅提升。三级分类目录逻辑classify_name为自定义业务分类更换参数即可切换存储目录同一爬虫脚本分别传入 nature、animal 等参数自动实现素材分文件夹归档CURR_DATE 依托 datetime 获取当日日期实现按抓取时间归档。stream 流式下载机制requests 请求添加streamTrue开启流式传输iter_content(chunk_size)按分片拉取图片字节避免超大尺寸高清图片一次性加载全量二进制至内存解决大批量下载时程序内存溢出崩溃问题。Pillow 图片校验机制Image.openverify () 校验图片文件头与二进制结构残缺、后缀篡改、空字节的损坏图片直接调用 os.remove 删除杜绝无效文件占用磁盘资源从源头保证本地图片资源可用。保留源文件名逻辑通过 URL 末尾分割获取原始图片名称相比自增序号命名可反向溯源图片来源链接便于后期素材整理、缺失资源补抓。3.4 进阶方案优缺点表格优点现存局限多级分类目录自动生成支持多类目分开存储文件管理规范化仅依靠正则匹配图片链接对 JS 动态渲染页面无法提取图片地址流式下载 图片校验适配高清大图批量抓取过滤损坏资源无自动延时逻辑高频连续请求易触发站点访问限制pathlib 跨平台适配Windows、Linux 部署无需修改路径代码未支持子文件夹自动分页递归抓取仅限单页面数据四、动态网页图片适配优化与反爬适配方案4.1 Referer 反爬场景适配大量 CDN 托管图片资源开启 Referer 防盗链策略请求头缺失来源字段直接返回 403 与空白图片解决方案分为静态指定 Referer 与动态从页面 URL 截取 Referer 两种。4.1.1 动态 Referer 代码片段python运行# 动态Referer赋值Referer为当前图片所在页面地址 HEADERS[Referer] TARGET_URL原理服务器通过 Referer 判断访问来源域名填充目标页面 URL 后模拟从站点内页面跳转访问图片资源绕过基础防盗链校验。4.2 异常场景处理补充代码磁盘空间不足捕获捕获 OSError 磁盘满载报错跳过当前图片并日志记录超长 URL 特殊文件名处理URL 末尾文件名含特殊字符无法创建文件时自动替换为自定义序号命名。python运行def safe_save_name(url, save_dir, idx): try: raw_name url.split(/)[-1] # 剔除文件名非法字符 invalid_char r\/:*?| for c in invalid_char: raw_name raw_name.replace(c, _) return save_dir / raw_name except: suffix url.split(.)[-1] return save_dir / ftemp_{idx}.{suffix}五、工程化封装图片爬虫工具类5.1 工具类完整代码python运行import requests, re, os from pathlib import Path from PIL import Image from datetime import datetime class ImageSpiderTool: def __init__(self, root_save_path./img_lib, uaNone): self.root Path(root_save_path) self.date_str datetime.now().strftime(%Y%m%d) self.headers { User-Agent: ua if ua else Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36 } def set_referer(self, ref_url): self.headers[Referer] ref_url def create_class_dir(self, class_name): dir_path self.root / class_name / self.date_str dir_path.mkdir(parentsTrue, exist_okTrue) return dir_path def extract_img_url(self, html): res re.findall(rhttps?://[^\s]\.(jpg|png|jpeg|webp), html) return list(set(res)) def check_img_valid(self, file_path): try: Image.open(file_path).verify() return True except: os.remove(file_path) return False def batch_download(self, page_url, class_name): save_dir self.create_class_dir(class_name) page_html requests.get(page_url, headersself.headers, timeout12).text img_list self.extract_img_url(page_html) for num, link in enumerate(img_list): save_path save_dir / link.split(/)[-1] try: resp requests.get(link, headersself.headers, streamTrue) with open(save_path, wb) as f: for chunk in resp.iter_content(8192): f.write(chunk) self.check_img_valid(save_path) except Exception as e: continue # 工具调用示例 if __name__ __main__: spider ImageSpiderTool(root_save_path./local_image) spider.set_referer(目标站点主页) spider.batch_download(page_url素材页面地址, class_namescenery)5.2 工具类落地优势工具类封装实现配置与业务逻辑解耦项目中多站点、多类目抓取仅需实例化对象并调用 batch_download 方法可被后续分页爬虫项目直接导入复用符合模块化开发规范。