一、问题来源
我日常写博客时,首先使用 typora 编辑 markdown 文件,然后在 CSDN 的 markdown 编辑器中导入 .md 文件。但是使用 typora 的小伙伴们都知道,typora 中缩放图片时使用的是 zoom 属性,然而 CSDN 并不支持这个属性。因此在导入用 typora 写好的 .md 文件时,在 CSDN 上观看时图片老大一个,并且不会自动居中,因此十分不爽。
二、解决方案
这里使用 python 正则匹配来替换所有图片对应的 url 内容。经过测试,只需要将 .md 文件放入项目工程中,运行 python 脚本后,把新生成的 .md 文件拷贝到 CSDN 中即可。下面介绍具体方案:
(一)创建工程项目
使用 pycharm 创建工程项目,在项目文件夹下创建两个文件夹 in 和 out。in 文件夹存储待处理的 .md 文件,out 文件夹为输出文件夹:
当然,如果你使用的是 vscode 或者其他 idle,类似即可,配置不复杂。
(二)代码
编写代码,这里我把我自己写的代码附上,仅供参考:
import re
file_name = "Unity核心8——模型导入.md" # 需要转换的文件名
default_scale = 50 # 图片默认的缩放百分比
zoom_scale = 0.5 # 整体调整原来 typora 中的 zoom 属性大小
# 水印相关参数
watermark = "x-oss-process=image/watermark" # 为图片添加图片或文字水印
type = "ZHJvaWRzYW5zZmFsbGJhY2s" # 文字水印字体(Base64编码)
shadow = "50" # 指定文字水印的阴影透明度
text = "Q1NETiBAemhlbGlrdQ==" # 文字水印内容(Base64编码)
size = "20" # 文字水印大小
color = "FFFFFF" # 文字水印颜色
t = "70" # 水印图片或水印文字的透明度
g = "se" # 水印位置
if_watermark = True # 是否为图片添加水印
def generate_watermark(): # 根据上述参数生成水印
return f"?{watermark},type_{type},shadow_{shadow},text_{text},size_{size},color_{color},t_{t},g_{g}"
def alter(s): # 处理未修改大小的图片
return '<img src="' + s.group(2) + (
generate_watermark() if if_watermark else "") + f'#pic_center" width="{default_scale}%">'
def alter_zoom(s): # 处理 typora 中用 zoom 属性修改过大小的图片
scale = zoom_scale * int(s.group(3))
return s.group(1) + (generate_watermark() if if_watermark else "") + "#pic_center" + s.group(
2) + f'width="{scale}%"' + s.group(4)
with open("./in/" + file_name, "r", encoding="utf-8") as fin, open("./out/" + file_name, "w", encoding="utf-8") as fout:
pattern = re.compile(r"(!\[image-\d+]\()(.+)(\))") # 未修改大小的图片的匹配规则
pattern_zoom = re.compile(r'(<img src=".*)(" .* )style="zoom:\s?(\d+)%;"(\s?/>)') # 带有 zoom 属性的图片的匹配规则
content = fin.read() # 读取文件内容
content1 = pattern.subn(alter, content) # 第一次处理
content2 = pattern_zoom.subn(alter_zoom, content1[0]) # 第二次处理
fout.write(content2[0]) # 写入新文件
print("原始图片替换次数:", content1[1])
print("zoom 属性图片替换次数:", content2[1])
print("总共替换次数:", content1[1] + content2[1])
关于水印参数部分,不清楚的可以参考 这篇文章
这里我将图片分为两种,一种是直接复制进 typora 的,即 ![…](…) 这种类型的链接图片。另一种是在 typora 中右键修改过大小的带有 zoom 属性的链接图片。
- 对于第一种图片,替换后默认缩放大小为 default_scale%;
- 对于第二种,原本是想保留原来的 zoom 属性值,直接替换为 width 属性。后来想想还是设置了一个缩放比例 zoom_scale。即,原来图片大小为 zoom: 50% 的图片,经过处理后变为 width: 50 * zoom_scale%
当然这里我的图片链接并不是本地的,我设置了云图床。如果你的图片链接是本地链接(即,使用了 assets 文件夹),那么复制到 CSDN 时可能转换失败。我这里没尝试过本地链接,在这里附上 typora 图床的配置教程
配置完成后,将 .md 文件放入 in 文件夹,修改 file_name,运行脚本后,直接把 out 文件夹中新生成的 .md 文件复制到 CSDN 的 markdown 编辑器即可。对应的图片大小自己微调就好。也可以自己修改代码调整参数自行配置~
今天上传时,发现并不能直接用 zoom 属性替换 width 的百分比属性,两个属性还是不一样的。因此在原来的代码基础上添加了获取图片大小的功能,将图片大小设置为固定值,该值为 原图大小 * zoom 属性值:
import io
import re
import urllib.request
from PIL import Image
file_name = "Unity核心8——模型导入.md" # 需要转换的文件名
scale = 0.6 # 图片整体缩放比例
# 水印相关参数
watermark = "x-oss-process=image/watermark" # 为图片添加图片或文字水印
type = "ZHJvaWRzYW5zZmFsbGJhY2s" # 文字水印字体(Base64编码)
shadow = "50" # 指定文字水印的阴影透明度
text = "Q1NETiBAemhlbGlrdQ==" # 文字水印内容(Base64编码)
size = "20" # 文字水印大小
color = "FFFFFF" # 文字水印颜色
t = "70" # 水印图片或水印文字的透明度
g = "se" # 水印位置
if_watermark = True # 是否为图片添加水印
def generate_watermark(): # 根据上述参数生成水印
return f"?{watermark},type_{type},shadow_{shadow},text_{text},size_{size},color_{color},t_{t},g_{g}"
def get_size(img_path): # 根据图片链接获取图片的大小
response = urllib.request.urlopen(img_path)
temp_img = io.BytesIO(response.read())
img = Image.open(temp_img)
return img.size
def alter(s): # 处理未修改大小的图片
w, h = get_size(s.group(2))
new_w, new_h = int(scale * w), int(scale * h)
return '<img src="' + s.group(2) + (
generate_watermark() if if_watermark else "") + f'#pic_center" width="{new_w}" height="{new_h}">'
def alter_zoom(s): # 处理 typora 中用 zoom 属性修改过大小的图片
zoom_value = int(s.group(4)) / 100
w, h = get_size(s.group(2))
new_w, new_h = int(zoom_value * w), int(zoom_value * h)
return s.group(1) + s.group(2) + (generate_watermark() if if_watermark else "") + "#pic_center" + s.group(
3) + f'width="{new_w}" height="{new_h}"' + s.group(5)
with open("./in/" + file_name, "r", encoding="utf-8") as fin, open("./out/" + file_name, "w", encoding="utf-8") as fout:
pattern = re.compile(r"(!\[image-\d+]\()(.+)(\))") # 未修改大小的图片的匹配规则
pattern_zoom = re.compile(r'(<img src=")(.*)(" .* )style="zoom:\s?(\d+)%;"(\s?/>)') # 带有 zoom 属性的图片的匹配规则
content = fin.read() # 读取文件内容
content1 = pattern.subn(alter, content) # 第一次处理
content2 = pattern_zoom.subn(alter_zoom, content1[0]) # 第二次处理
fout.write(content2[0]) # 写入新文件
print("原始图片替换次数:", content1[1])
print("zoom 属性图片替换次数:", content2[1])
print("总共替换次数:", content1[1] + content2[1])
这次又改进了一下代码,现在可以根据 typora 中图片的大小来进行缩放后,上传到 CSDN。
- 在
alter
函数中,width = int(w * scale / max_width * 100)
。
typora 中的原始图片宽度为 w,我们默认宽度 max_width = 960 为标准宽度,即对应的 width=100%。因此计算后,可依据 scale 缩放因子来调节 CSDN 中的 width 大小。 - 在
alter_zoom
函数中,width = int(w * scale * zoom_value / max_width * 100)
。
typora 中的原始图片宽度为 w * zoom_value,即多乘了一个 typora 中的 zoom 属性。后续计算一样。
用的时候可以更改 scale 的值来调整你想要的全局缩放大小。
import io
import re
import urllib.request
from PIL import Image
file_dir = "./in" # 需要转换的文件夹
scale = 1.2 # 图片整体缩放比例
use_width_percent = True # 是否使用 width 百分比属性放缩
max_width = 960 # 标准图片宽度参考值
# 水印相关参数
watermark = "x-oss-process=image/watermark" # 为图片添加图片或文字水印
type = "ZHJvaWRzYW5zZmFsbGJhY2s" # 文字水印字体(Base64编码)
shadow = "50" # 指定文字水印的阴影透明度
text = "Q1NETiBAemhlbGlrdQ==" # 文字水印内容(Base64编码)
size = "20" # 文字水印大小
color = "FFFFFF" # 文字水印颜色
t = "70" # 水印图片或水印文字的透明度
g = "se" # 水印位置
if_watermark = False # 是否为图片添加水印
def generate_watermark(): # 根据上述参数生成水印
return f"?{watermark},type_{type},shadow_{shadow},text_{text},size_{size},color_{color},t_{t},g_{g}"
def get_size(img_path): # 根据图片链接获取图片的大小
response = urllib.request.urlopen(img_path)
temp_img = io.BytesIO(response.read())
img = Image.open(temp_img)
# print(img.size)
return img.size
def alter(s): # 处理未修改大小的图片
w, h = get_size(s.group(2))
new_w, new_h = int(scale * w), int(scale * h)
width = int(w * scale / max_width * 100)
if use_width_percent:
return '<img src="' + s.group(2) + (
generate_watermark() if if_watermark else "") + f'#pic_center" width="{width}%">'
else:
return '<img src="' + s.group(2) + (
generate_watermark() if if_watermark else "") + f'#pic_center" width="{new_w}" height="{new_h}">'
def alter_zoom(s): # 处理 typora 中用 zoom 属性修改过大小的图片
zoom_value = int(s.group(4)) / 100
w, h = get_size(s.group(2))
new_w, new_h = int(zoom_value * w), int(zoom_value * h)
width = int(w * scale * zoom_value / max_width * 100)
if use_width_percent:
return s.group(1) + s.group(2) + (generate_watermark() if if_watermark else "") + "#pic_center" + s.group(
3) + f'width="{width}%"' + s.group(5)
else:
return s.group(1) + s.group(2) + (generate_watermark() if if_watermark else "") + "#pic_center" + s.group(
3) + f'width="{new_w}" height="{new_h}"' + s.group(5)
file_name = "第24章 预处理指令.md"
with open("./in/" + file_name, "r", encoding="utf-8") as fin, open("./out/" + file_name, "w",
encoding="utf-8") as fout:
pattern = re.compile(r"(!\[image-\d+]\()(.+)(\))") # 未修改大小的图片的匹配规则
pattern_zoom = re.compile(r'(<img src=")(.*)(" .* )style="zoom:\s?(\d+)%;"(\s?/>)') # 带有 zoom 属性的图片的匹配规则
content = fin.read() # 读取文件内容
content1 = pattern.subn(alter, content) # 第一次处理
content2 = pattern_zoom.subn(alter_zoom, content1[0]) # 第二次处理
fout.write(content2[0]) # 写入新文件
print("原始图片替换次数:", content1[1])
print("zoom 属性图片替换次数:", content2[1])
print("总共替换次数:", content1[1] + content2[1])
三、项目工程文件
GitHub 仓库地址:https://github.com/zheliku/CSDN_with_Typora。
四、问题遗留
近期使用 python 生成的 .md 直接复制到 CSDN 上传发布后,CSDN 总是会将图片链接中的 #pic_center 删除,导致每次都需要重新在 CSDN 里面手打每一张图片的 #pic_center,这样上传后才能正常居中显示。有解决办法的小伙伴可以评论留言,感激不尽~