简介:这份PS素材包面向Photoshop初学者与有一定基础的设计爱好者,用于解决日常创作中笔刷、样式、图案等预设资源不足、需从零搭建效果的问题,可服务于平面设计、摄影后期与视觉创作等场景。压缩包共收录135个文件,约32MB,以62个png与57个jpg图片素材为主,另含9个psd分层源文件便于二次编辑,以及少量avi演示视频、swf与html页面等辅助内容,覆盖图层样式、动作、笔刷、渐变、图案、形状等常见预设类别。目前已有504人学习下载,具备一定的使用参考价值。解压后可通过预设管理器分类导入,直接套用于图层与绘画流程,减少重复制作细节的时间,帮助读者快速丰富设计工具箱、提升出图效率与作品完成度,业余爱好者与专业设计师均可按需取用。
1. 一个叫「PS素材.rar」的压缩包,真正值钱的是什么
拿到一个名为「PS素材.rar」的压缩包,多数人的第一反应是解压、翻文件夹、看有没有能直接拖进画布的 PSD。这个动作没错,但只做这一步,基本等于把一箱零件当成品用。素材包的本质不是「图」,而是「可复用的图层结构 + 可替换的占位内容 + 一套命名约定」。真正决定它能不能进生产流程的,是解压之后你有没有能力把它变成一套可检索、可批量替换、可版本管理的资源库。
这篇面向的是手里已经有一个素材压缩包、想把它用进日常出图或团队协作的人:设计师想知道怎么快速筛出能用的,前端或运营想知道怎么批量改文案改尺寸,工程侧想知道怎么把一堆 PSD 变成可程序化处理的资产。下面按「先看清结构、再动手拆、然后建索引、最后避坑」的顺序讲,每一步都给能直接跑的脚本和参数。
2. 解压前先看清结构:PS素材.rar 里到底装了什么
很多人双击就解压,结果几万个文件糊在一个目录里,后面想按用途分类已经晚了。正确顺序是先列清单、再决定解压策略。RAR 格式在 Linux 下常用unrar或7z处理,Windows 下7z也能读。先做一次「只读清单」,不落盘,看清目录深度和文件类型分布。
2.1 用 7z 列出压缩包清单并统计文件类型
# 只列清单,不解压;-slt 输出详细字段,便于后续解析 7z l -slt "PS素材.rar" > manifest.txt # 统计扩展名分布,快速判断这是纯 PSD 包还是混了笔刷、动作、字体 grep -i '^Path = ' manifest.txt \ | sed 's/^Path = //' \ | awk -F. 'NF>1{print tolower($NF)}' \ | sort | uniq -c | sort -rn | head -20第一段命令把压缩包内每个条目的路径、大小、修改时间等字段导出成文本,不产生解压文件,适合先摸底。-slt是「技术列表」模式,字段以Key = Value形式排列,比默认表格好解析。第二段从清单里抽Path行,取最后一个点后的扩展名做小写归一,再计数排序。你会看到psd、jpg、png、abr(笔刷)、atn(动作)、pat(图案)各自的占比。如果psd占比高但单个文件很大,说明是分层源文件;如果jpg占绝大多数,那这个包更可能是预览图集合,分层价值有限,别抱太高期待。
提示:清单里如果出现大量
__MACOSX/或.DS_Store,说明打包方用的是 Mac,解压后要过滤掉这些垃圾条目,否则后面建索引会被污染。
2.2 判断 PSD 是否真的分层:看文件头和图层数
扩展名是.psd不代表它分层。有些「素材」其实是扁平化导出的 PSD,打开只有一个背景层。判断办法有两个:看文件大小(分层 PSD 通常明显大于同尺寸 JPG),以及直接读 PSD 头部的图层计数。Python 里用psd-tools最省事。
from psd_tools import PSDImage import os def inspect_psd(path): try: psd = PSDImage.open(path) layers = list(psd.descendants()) # 递归拿所有子图层 return { "file": os.path.basename(path), "size": f"{psd.width}x{psd.height}", "layers": len(layers), "groups": sum(1 for l in layers if l.is_group()), "text_layers": sum(1 for l in layers if l.kind == "type"), } except Exception as e: return {"file": os.path.basename(path), "error": str(e)} # 对解压目录做一次扫描,输出前 10 个分层最多的文件 results = [inspect_psd(os.path.join(root, f)) for root, _, files in os.walk("./extracted") for f in files if f.lower().endswith(".psd")] results = [r for r in results if "error" not in r] for r in sorted(results, key=lambda x: x["layers"], reverse=True)[:10]: print(r)PSDImage.open只读文件头,不渲染像素,速度快。descendants()递归展开图层组,is_group()区分组和普通层,kind == "type"能数出文本层数量——文本层多说明这个 PSD 适合做模板改字,文本层为零说明改文案得靠重新排版。width/height用来判断尺寸是否够用。这段脚本的价值在于:几百个 PSD 里,你能一眼挑出「图层多、有文本层、尺寸达标」的那批,剩下的扁平文件直接归到「参考图」而不是「模板」。
2.3 解压策略:按扩展名分流,别一次性全铺开
摸底之后再做解压,并且按类型分目录。7z支持通配符提取,可以只解 PSD,把笔刷、动作先留在包里。
mkdir -p extracted/psd extracted/preview extracted/other # 只解 PSD 到 psd 目录,保留压缩包内相对路径 7z x "PS素材.rar" "*.psd" -oextracted/psd -y # 预览图单独放,方便快速浏览 7z x "PS素材.rar" "*.jpg" "*.png" -oextracted/preview -y # 其余(笔刷、动作、字体说明等)最后处理 7z x "PS素材.rar" -x'!*.psd' -x'!*.jpg' -x'!*.png' -oextracted/other -y-o指定输出目录,-y跳过覆盖确认。第一条只提*.psd,第二条提预览图,第三条用-x!排除已处理的类型,把剩下的收进other。这样做的原因是:PSD 体积大、处理慢,先单独放;预览图小、用来肉眼筛;笔刷动作这类需要装进软件目录的,最后单独处理,避免和源文件混在一起。分流之后,后面建索引、批量替换都只针对extracted/psd,范围可控。
3. 把 PSD 变成可批量替换的模板:图层命名与占位约定
素材包能不能进生产,分水岭在于「图层名是否可被程序识别」。一个图层叫「图层 1 拷贝 3」,程序没法知道它是标题还是按钮;叫TXT_TITLE、IMG_HERO,就能被脚本定位替换。这一章讲怎么把混乱的图层名规整成约定,以及怎么用脚本做批量替换。
3.1 先审计图层命名,找出可自动化的部分
不要一上来就改,先统计现有命名模式。下面脚本输出每个 PSD 的图层名样本和「疑似占位层」数量(名字里含 text、title、img、logo、btn 等关键词的)。
from psd_tools import PSDImage import re, os PLACEHOLDER = re.compile(r"(txt|text|title|head|img|image|photo|logo|btn|button|bg)", re.I) def audit(path): psd = PSDImage.open(path) names = [l.name for l in psd.descendants() if l.name] hits = [n for n in names if PLACEHOLDER.search(n)] return len(names), len(hits), hits[:5] for f in sorted(os.listdir("./extracted/psd")): if f.lower().endswith(".psd"): total, hits, sample = audit(os.path.join("./extracted/psd", f)) print(f"{f}: layers={total}, placeholder_like={hits}, sample={sample}")正则里把常见占位词收进去,re.I忽略大小写。输出里placeholder_like占比高的 PSD 就是「天生适合做模板」的,占比低甚至为零的,要么是纯视觉稿,要么命名完全没规范,改造成本高,先放一边。sample打印前五个命中名,让你快速判断命名风格是英文下划线还是中文描述。这一步是筛选,不是改造,目的是把有限精力投到回报最高的文件上。
3.2 用 psd-tools 做文本层批量替换
确认某个 PSD 有文本层且命名可识别后,就能做批量改字。下面是最小可跑示例:把名字匹配TXT_的文本层内容替换成新文案,另存为新文件,不动原稿。
from psd_tools import PSDImage from psd_tools.api.layers import TypeLayer def replace_text(src, dst, mapping): psd = PSDImage.open(src) changed = 0 for layer in psd.descendants(): if isinstance(layer, TypeLayer) and layer.name in mapping: layer.text = mapping[layer.name] # 直接改文本内容 changed += 1 psd.save(dst) return changed mapping = { "TXT_TITLE": "春季新品上市", "TXT_SUB": "限时体验价", "TXT_BTN": "立即查看", } n = replace_text("./extracted/psd/banner_a.psd", "./out/banner_a_new.psd", mapping) print("replaced layers:", n)TypeLayer是 psd-tools 里专门表示文本层的类,layer.text可读可写,赋值后psd.save会重新写回 PSD 结构。mapping的键必须和图层名完全一致(大小写敏感),所以上一步的命名审计很关键。changed计数用来确认到底改到了几层——如果返回 0,说明名字对不上,别急着怀疑库,先回去看图层名。注意:改文本不会自动重排字号和换行,长文案可能溢出文本框,这是 PSD 模板的固有边界,后面避坑章会讲怎么处理。
3.3 图片占位层替换:智能对象的正确姿势
比文本更容易翻车的是图片替换。PSD 里的图片占位通常是「智能对象」层,直接改像素会破坏缩放关系。psd-tools 对智能对象的写入支持有限,稳妥做法是「导出占位层尺寸 → 外部按尺寸裁好图 → 用脚本替换图层像素」。先拿到占位层的边界和尺寸。
from psd_tools import PSDImage psd = PSDImage.open("./extracted/psd/card_template.psd") for layer in psd.descendants(): if layer.name.startswith("IMG_"): bbox = layer.bbox # (left, top, right, bottom) w, h = bbox[2]-bbox[0], bbox[3]-bbox[1] print(layer.name, "size:", w, "x", h, "bbox:", bbox)bbox给出图层在画布中的像素边界,宽高就是你需要准备的图片尺寸。拿到尺寸后,用 Pillow 把新图裁成同比例再替换,能避免拉伸。如果占位层是智能对象且 psd-tools 写入报错,退一步的做法是:在 PSD 里把智能对象转成普通图层再存一份「可编程副本」,原稿保留不动。这是血泪经验——直接改智能对象十有八九会遇到结构写回失败,留一份扁平副本专门给脚本用,比跟库的兼容性较劲划算。
4. 给素材建索引:让「PS素材.rar」变成可检索资源库
素材一多,找图就靠记忆和翻文件夹,效率极低。建一个轻量索引,把「文件名、尺寸、图层数、主色、标签」落到一张表里,后面无论是自己查还是团队共享,都能秒定位。这一章用 SQLite 做存储,不引入重型服务。
4.1 设计索引表结构
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INTEGER PK | 自增主键 |
| path | TEXT UNIQUE | 相对路径,唯一约束防重复入库 |
| width / height | INTEGER | 画布尺寸,用于按尺寸筛选 |
| layers | INTEGER | 图层总数,判断复杂度 |
| text_layers | INTEGER | 文本层数,判断是否可改字 |
| dominant_color | TEXT | 主色十六进制,用于配色检索 |
| tags | TEXT | 逗号分隔的人工/自动标签 |
| mtime | INTEGER | 文件修改时间戳,用于增量更新 |
表结构刻意保持扁平,tags用逗号分隔而不是单独建关联表,是因为素材库规模通常在几千到几万,扁平表查询足够快,维护成本低。path加唯一约束,重复扫描时用INSERT OR REPLACE就能幂等更新。dominant_color用主色做检索是个实用技巧——运营常说「找一张蓝色调的背景」,有主色字段就能直接筛。
4.2 扫描目录并写入索引
import sqlite3, os, time from psd_tools import PSDImage from PIL import Image from collections import Counter conn = sqlite3.connect("assets.db") conn.execute("""CREATE TABLE IF NOT EXISTS assets( id INTEGER PRIMARY KEY, path TEXT UNIQUE, width INTEGER, height INTEGER, layers INTEGER, text_layers INTEGER, dominant_color TEXT, tags TEXT, mtime INTEGER)""") def dominant(path): img = Image.open(path).convert("RGB").resize((64, 64)) c = Counter(img.getdata()).most_common(1)[0][0] return "#%02x%02x%02x" % c for root, _, files in os.walk("./extracted/psd"): for f in files: if not f.lower().endswith(".psd"): continue p = os.path.join(root, f) try: psd = PSDImage.open(p) layers = list(psd.descendants()) conn.execute( "INSERT OR REPLACE INTO assets(path,width,height,layers,text_layers,dominant_color,tags,mtime) VALUES(?,?,?,?,?,?,?,?)", (p, psd.width, psd.height, len(layers), sum(1 for l in layers if l.kind == "type"), dominant(psd.composite()), "", int(os.path.getmtime(p)))) except Exception as e: print("skip", p, e) conn.commit()psd.composite()把 PSD 合成一张预览图,再缩到 64×64 取众数颜色,比逐像素统计快得多,精度对「找主色调」足够。INSERT OR REPLACE依赖path的唯一约束实现幂等,重复跑不会产生重复行。异常捕获是为了跳过损坏文件,不让一个坏文件中断整批扫描。tags先留空,后面可以人工补,也可以按目录名自动填。
4.3 用 SQL 做实际检索
索引建好后,日常查询就是几条 SQL 的事。下面几个是高频场景。
-- 找尺寸够大、图层多、带文本层的可改字模板 SELECT path, width, height, layers, text_layers FROM assets WHERE width >= 1200 AND text_layers >= 2 ORDER BY layers DESC LIMIT 20; -- 按主色找蓝色系背景 SELECT path, dominant_color FROM assets WHERE dominant_color LIKE '#1%' OR dominant_color LIKE '#2%' LIMIT 30; -- 找出长期没动过的素材,考虑清理 SELECT path, datetime(mtime,'unixepoch') AS updated FROM assets ORDER BY mtime ASC LIMIT 20;第一条把「大尺寸 + 多文本层」作为模板筛选条件,直接对应改字场景。第二条用十六进制前缀粗筛蓝色系,虽然不精确,但比肉眼翻文件夹快一个量级,命中后再人工确认。第三条按修改时间排序,用来识别僵尸素材。这些查询不需要额外工具,任何支持 SQLite 的客户端都能跑,团队共享时把assets.db放共享盘即可。
5. 避坑与排查:PS素材.rar 处理中最容易翻车的五件事
5.1 解压后中文文件名乱码
现象:解压出来的 PSD 名字变成一串问号或乱码,脚本按名字匹配全部失效。原因:RAR 打包时的编码和本机解压工具的默认编码不一致,常见于跨系统打包。解决:用7z时加-mcp=936指定代码页,或在unrar里用-p配合系统区域设置;更稳的做法是解压后统一用脚本按「文件内容哈希」重命名,彻底摆脱对原始文件名的依赖。
5.2 psd-tools 读大文件内存爆掉
现象:扫描到某个几百 MB 的 PSD 时进程被系统杀掉。原因:psd.composite()会把整张图渲染进内存,超大画布叠加多图层时内存占用陡增。解决:索引阶段不要对每个文件都做合成,先只读头部拿尺寸和图层数,主色提取只对小于阈值(比如 50MB)的文件做;大文件标记为「待人工处理」,避免批量任务被单个文件拖垮。
5.3 文本替换后字号溢出文本框
现象:替换成长文案后,文字超出原文本框,导出图被裁切。原因:PSD 文本层有固定的段落框尺寸,改内容不会自动缩放字号。解决:替换前先读layer.text的原始长度,按比例估算新文案是否需要缩字号;psd-tools 可改layer.font_size,但多行文本的换行仍需人工确认。稳妥流程是「脚本改字 → 导出预览 → 人工抽检」,别指望全自动零校对。
5.4 智能对象写入失败或图层错位
现象:替换图片后保存报错,或保存成功但打开发现图层位置偏移。原因:智能对象内部结构复杂,psd-tools 的写入支持不完整,且替换像素时没考虑图层自身的变换矩阵。解决:如 3.3 所述,保留一份「智能对象转普通图层」的可编程副本专供脚本使用;替换时用bbox对齐而不是直接贴图,替换后必须打开抽检,不能只看脚本返回成功。
5.5 索引重复入库导致检索结果翻倍
现象:同一个文件在查询结果里出现两次。原因:path用了绝对路径,换一次工作目录或挂载点变化就变成新记录;或者唯一约束没生效。解决:入库前统一转成相对于素材根目录的路径,os.path.relpath处理;建表时确认path TEXT UNIQUE真的写进去了,老库可以用CREATE UNIQUE INDEX补。增量扫描时先按mtime比对,没变的跳过,减少无谓写入。
6. 进阶:把素材库接进自动化出图流水线
单机索引解决的是「找得到」,再往前一步是「自动出图」。思路是把第 3 章的替换脚本和第 4 章的索引串起来:给定一个模板 ID 和一组文案/图片,脚本查索引拿到路径,执行替换,导出 PNG,全程不打开设计软件。下面是一个最小流水线示例,用配置驱动,方便非开发同事改参数。
import json, sqlite3, os from psd_tools import PSDImage from psd_tools.api.layers import TypeLayer def render(template_id, payload, out_dir="./out"): conn = sqlite3.connect("assets.db") row = conn.execute("SELECT path FROM assets WHERE id=?", (template_id,)).fetchone() if not row: raise ValueError(f"template {template_id} not found") psd = PSDImage.open(row[0]) for layer in psd.descendants(): if isinstance(layer, TypeLayer) and layer.name in payload.get("text", {}): layer.text = payload["text"][layer.name] os.makedirs(out_dir, exist_ok=True) out = os.path.join(out_dir, f"{template_id}_{payload.get('batch','default')}.png") psd.composite().save(out) return out job = json.load(open("job.json")) print(render(job["template_id"], job))job.json里放template_id、text映射和批次名,运营改 JSON 就能出图,不用碰代码。psd.composite()导出合成图,格式由扩展名决定。这条流水线的边界要说清楚:它适合「文案替换 + 固定版式」的批量场景,比如电商主图、活动 banner 的多语言版本;涉及图片替换、复杂排版调整的,仍然要回到设计软件。我一般会把「能自动化的」和「必须人工的」在模板设计阶段就分开——图层命名规范、占位层尺寸固定、文本层不跨行,这三条守住了,自动化才稳。
验证方法很简单:拿同一个模板跑 20 组不同长度的文案,逐张看导出图有没有溢出、错位、缺字。这一步别省,我吃过亏——脚本全绿但导出图里标题被裁掉一半,返工成本比手工做还高。现在的习惯是:任何批量出图任务,先跑 5 张抽检,确认版式没崩再放量。素材包本身不会告诉你这些边界,只有跑过一轮才知道哪些模板能进流水线、哪些只能当参考图。希望帮到你。
本文还有配套的精品资源,点击获取