EagleBear2002 的博客

这里必须根绝一切犹豫,这里任何怯懦都无济于事

批量清除博客中未使用的图片

问题背景

在整理博客过程中,有时需要把博客当中的图片批量替换成更清晰或文件大小更小的图片,这时原有的图片不会被删除,而是被留在原本的目录下。这样的“未使用的图片”积攒的太多,会拖慢博客部署的速度,也会导致博客仓库占用空间膨胀。

例如:笔者在 技术科普 这一主题下写了一篇文章 技术科普/异体汉字成因及处理.md,则该文章引用的所有图片都被放在与文章同名的 技术科普/异体汉字成因及处理 目录下。这一目录下有 1.png, 2.png, 3.png 等图片,其中我真正使用到的只有 3.png。我希望批量删除 1.png, 2.png 等图片。

本文提供了以上问题的两种解决方案。对于需要一次对若干篇文章清除图片的用户,推荐使用方案二。

解决方案一:利用 Typora 的文件重命名机制

Typora 编辑器的重命名机制:在编辑器侧边栏的 Files 选项卡中对当前打开的文件进行重命名,操作成功后,文章内引用的图片会自动被移动到与新文件名同名的目录下。

  1. 编辑器中打开 技术科普/异体汉字成因及处理.md 后,侧边栏中灰色背景强调的文件是当前打开的文件;
  2. 技术科普/异体汉字成因及处理.md 文件重命名为 技术科普/异体汉字成因及处理-new.md,则文件中引用的图片路径 技术科普/异体汉字成因及处理/3.png 会被自动修改成 技术科普/异体汉字成因及处理-new/3.png,该图片也会自动被移动到新的目录中。
  3. 此时,旧的图片目录 技术科普/异体汉字成因及处理/ 中已经不再有被文章引用的图片,可以直接删除整个目录。
  4. 最后,按照 2. 中的方法,将文章名重新改回 技术科普/异体汉字成因及处理.md 即可。

经过以上 4 个步骤,所有“未使用的图片”已经被清楚,而所有被引用的图片都会被保留。

该解决方案的弊端在于:当文件特别大(8000 字)且图片特别多(50 张)时,每次重命名耗时较长,建议采用解决方案二。

解决方案二:使用 python 脚本寻找并删除未使用的图片

该脚本文件放在任意位置,但要在 hexo 博客根目录下执行脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import os
import glob
import re
import urllib.parse

SEP = "\\"
# 定义 markdown 文件路径
prefixPath = "source" + SEP + "_posts"
folderName = "技术科普"
fileName = "异体汉字成因及处理"
imageFolderPath = prefixPath + SEP + folderName + SEP + fileName
mdFilePath = imageFolderPath + ".md"

print("Markdown Path: " + mdFilePath)

# 获取 markdown 文件中引用的图片路径
with open(mdFilePath, 'r', encoding='utf-8') as f:
content = f.read()

# 使用正则表达式提取图片路径
# 匹配 Markdown 语法 ![alt_text](image_path) 和 HTML 语法 <img src="image_path" alt="alt_text">
image_paths = re.findall(r'!\[.*?\]\((.*?)\)|<img[^>]+src=["\'](.*?)["\']', content)

# 将图片路径列表展开成一维列表
# image_paths = [path for group in image_paths for path in group if path]
image_paths = [urllib.parse.unquote(prefixPath + SEP + folderName + SEP + path).replace("/", "\\") for group in image_paths for path in group if path]

# 获取资源目录下的所有图片文件
image_files = glob.glob(imageFolderPath + SEP + "*")

# 删除未被引用的图片文件
for file in image_files:
if file not in image_paths:
os.remove(file)
print("delete " + file) # 输出被删除的图片路径
else:
print("saved " + file) # 输出被保留的图片路径


print("Successfully remove unused images in " + mdFilePath)