将jupyter notebook和Rmarkdown生成的markdown文件中图片的相对地址自动修改为图床地址(Python and R实现)
- 使用 jupyter notebook 写数据分析报告,并将报告导出为 md 文件;该 md 文件稍作修改便可上传到博客如 CSDN,避免了在博客中重新组织代码和结果。
- 但若在 jupyter notebook 中画图,则导出 md 文件时会是一压缩文件夹,文件夹内是 md 文件以及画图生成的图片,md 文件内图片使用的是本地地址。
- md 文件上传到博客时,需要将图片本地地址更换为相应的图床地址才可正确显示,若 jupyter notebook 生成图片太多,手工完成这个工作比较耗时,因此可通过 python 编程实现这个过程。基本思路是:将生成的图片重命名以使得所有图片名具有唯一性;将 md 文件中图片的本地路径修改为相应的图床路径;将图片上传到图床。
- 使用 R 语言的 Rmarkdown 写数据分析报告,导出的 md 文件中图片也使用本地地址,可按相同思路解决(脚本见文章末尾)。
import os
import re
import time
import copy
本地图片重命名
jupyter notebook 中若有图片生成,则下载 md 文件时会是一个压缩文件夹,压缩文件夹内是 md 文件以及画图生成的图片
文件夹内容
output_all = os.listdir("./")
output_all
文件夹内的图片:假设生成了这 3 个图片
output_png = []
for i in output_all:
if i[-3:] == "png":
output_png.append(i)
output_png
['output_1_0.png', 'output_2_0.png', 'output_3_0.png']
图片重命名
对文件夹内的每一个图片进行重命名:新图片名 = 图片名 + 当前时间 + 1/2/3/...;目的是让每一张图片名具有唯一性
rand = int(time.time())
rand
1594719279
每个图片及其对应的时间标识
num_png = []
num_png_dict = {}
for i, j in enumerate(output_png):
num_png.append(j[:-4] + str(rand + i) + ".png")
num_png_dict[j] = str(rand + i)
num_png_dict_copy = copy.deepcopy(num_png_dict)
num_png_dict_copy
{'output_1_0.png': '1594719279',
'output_2_0.png': '1594719280',
'output_3_0.png': '1594719281'}
对图片重命名
for i in num_png_dict.items():
os.rename("./" + i[0], "./" + i[0][:-4] + i[1] + ".png")
md 文件内图片本地地址更改为图床地址
读入 md 文件内容
md_content 为字符串
with open("./test.md", encoding="utf-8") as f:
md_content = f.read()
查找 md 文件内容中的图片
md_png_rude = re.findall(".*.png", md_content)
md_png_rude
['![png](output_1_0.png', '![png](output_2_0.png', '![png](output_3_0.png']
md_png = [i.split("(")[1] for i in md_png_rude]
md_png
['output_1_0.png', 'output_2_0.png', 'output_3_0.png']
将图片本地地址更改为图床地址
修改后的图床地址如下,https://**/img/为图床地址
url_set = []
for i in num_png_dict_copy.items():
url = "https://**/img/" + i[0][:-4] + i[1] + ".png"
url_set.append(url)
md_content = md_content.replace(i[0], url)
url_set
['https://**/img/output_1_01594719279.png',
'https://**/img/output_2_01594719280.png',
'https://**/img/output_3_01594719281.png']
将修改后的 md 文件内容保存
with open("./new_test.md", mode="w", encoding="utf-8") as f:
f.write(md_content)
最后将重命名后的图片上传到图床
上传到图床后,就可以发现 md 文件中的图片正常显示了
图床工具:PicGo
这里使用的图床工具是 PicGo
该工具有以下特点:
- 支持图片批量上传
**.png
的图片上传到图床后,对应的图床地址为:图床地址/**.png
(确保 PicGo 设置中,未选中时间戳重命名)- 图片上传时,若图片中已有跟上传图片相同名称的图片,上传会失败;这也是对图片重命名,让每一个图片名具有唯一性的原因
其他
- 该文章即是用 jupyter notebook 写作,导出 md 文件后上传
- 对于 jupyter notebook 中非画图生成的图片,使用时直接使用图床地址,导出的 md 文件中也是图床地址
- 为了更加便捷,可以将本 Code 组织成 py 文件,并使用 fire 库(可参考博客)将 py 文件所需要的参数暴露给命令行,以后就可以使用命令行完成这个过程
py 文件如下
使用方法
- jupyter notebook 下载 md 文件时下载到 zip 文件夹
- 将该 zip 文件夹解压
- 将该 py 文件放入到解压文件夹中(注意修改为自己的图床地址)
- 打开 cmd,cd 到该解压文件夹路径,运行命令
python **.py **.md
,**.py
为 py 文件的名称,**.md
为待处理的 md 文件,这样就得到了处理好的 md 文件
import os
import re
import time
import copy
import fire
def convert_md(md_name):
output_all = os.listdir("./")
output_png = []
for i in output_all:
if i[-3:] == "png":
output_png.append(i)
rand = int(time.time())
num_png = []
num_png_dict = {}
for i, j in enumerate(output_png):
num_png.append(j[:-4] + str(rand + i) + ".png")
num_png_dict[j] = str(rand + i)
num_png_dict_copy = copy.deepcopy(num_png_dict)
for i in num_png_dict.items():
os.rename("./" + i[0], "./" + i[0][:-4] + i[1] + ".png")
with open("./" + md_name, encoding="utf-8") as f:
md_content = f.read()
md_png_rude = re.findall(".*.png", md_content)
md_png = [i.split("(")[1] for i in md_png_rude]
url_set = []
for i in num_png_dict_copy.items():
# 修改为自己的图床地址
url = "https://**/img/" + i[0][:-4] + i[1] + ".png"
url_set.append(url)
md_content = md_content.replace(i[0], url)
with open("./new_" + md_name, mode="w", encoding="utf-8") as f:
f.write(md_content)
if __name__ == '__main__':
fire.Fire(convert_md)
修改 Rmarkdown 导出 md 中图片路径的方法
- 将下面脚本保存为
*.R(如convert.R)
,放在 Rmarkdow*.Rmd(如test.Rmd)
文件所在的根目录 - cmd 中 cd到根目录
- cmd 中运行
Rscript "convert.R" "test.Rmd"
然后发现根目录中生成了 test.md
和 new_test.md
文件,test.md
文件中的图片使用本地路径,new_test.md
文件中的图片使用图床路径;另注意,该脚本是在 Rmarkdown 中未设置 fig.path
参数(决定画图生成图片的保存路径)下写的,这样 Rmarkdown 转 md 后默认将图片保存在根目录的 figure 文件夹中。
# -------------------------------------------------------------------------
# 将Rmarkdown转为Markdown:`*.Rmd`-->`*.md`
# -------------------------------------------------------------------------
rm(list = ls()) # 清空工作空间!!!
# 从命令行传入该脚本所需要的参数:Rmarkdown的文件名;如`test.Rmd`
args <- commandArgs(trailingOnly = T)
Rmd_name <- args
md_name <- paste0(unlist(strsplit(Rmd_name, "."))[1], ".md")
knitr::knit(input = paste0("./", Rmd_name),
output = paste0("./", md_name))
# -------------------------------------------------------------------------
# 将`*.md`中图片的本地路径转为图床路径
# -------------------------------------------------------------------------
library(readr)
library(stringr)
# 当前时间距离1970-01-01 00:00:00的秒数
rand <- trunc(as.numeric(difftime(Sys.time(),
as.POSIXlt("1970-01-01 00:00:00"),
units = "s")))
# 加入该秒数使得图片名唯一
png_output <- paste0("./figure/", list.files(path = "./figure"))
for (i in 1:length(png_output)) {
png_i_name <- unlist(strsplit(png_output[i], split = ".png"))
file.rename(from = png_output[i], to = paste0(png_i_name, rand, ".png"))
}
# -------------------------------------------------------------------------
# 读入`*.md`为字符串
md <- read_file(paste0("./", md_name))
# 从字符串中提取Rcode生成图片的名称
fig_png <- unlist(str_extract_all(string = md,
pattern = "figure.*.png"))
# 将`*.md`中图片的本地路径修改为图床路径
# https://**/img/为图床地址
for (i in 1:length(fig_png)) {
png_i_name <- strsplit(unlist(strsplit(fig_png[i], split = "/"))[2],
split = ".png")
md <- str_replace_all(string = md,
pattern = fig_png[i],
replacement = paste0(
"https://**/img/",
png_i_name, rand, ".png"))
}
# 保存为新的md文件,打开后发现图片已全部修改为图床路径
write_file(md, paste0("./new_", md_name))