Markdown解析图片路径、上传图床并替换路径

开始

  • 首先假设你hexo new title了一篇文章,并写好了内容:
1
2
3
4
5
...
![](~/img/1.png)
![](~/img/2.png)
![](~/img/3.png)
...
  • 如果你的博客托管在GitHub上,博客的图片需要请求很久才能显示出来。这是因为上传到GitHub的图片往往需要请求海外服务器,而访问图床的通讯代价比较大。
  • 为此,我们需要将图片放到一个高速的图床服务器上,将图片与文本分别存储在不同的服务器上。本篇博客我推荐sm.ms图床(免费)。

上传图片到sm.ms

  • 调用sm.ms的API,并将调用结果返回
1
2
3
4
5
6
7
8
9
10
11
12
13
def post_img(filePath):
headers = {
'Authorization': '你的API ID',
}
try:
data = {
'smfile': (filePath.split('/')[-1], open(filePath, 'rb')),
'format': 'json'
}
except:
return False
res = requests.post('https://sm.ms/api/v2/upload', headers=headers, files=data).text
return json.loads(res)
  • 调用成功后的返回内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"success": true,
"code": "success",
"message": "Upload success.",
"data": {
"file_id": 0,
"width": 4677,
"height": 3307,
"filename": "luo.jpg",
"storename": "D5VpWCKFElUsPcR.jpg",
"size": 801933,
"path": "/2019/12/16/D5VpWCKFElUsPcR.jpg",
"hash": "Q6vLIbCGZojrMhO2e7BmgFuXRV",
"url": "https://vip1.loli.net/2019/12/16/D5VpWCKFElUsPcR.jpg",
"delete": "https://sm.ms/delete/Q6vLIbCGZojrMhO2e7BmgFuXRV",
"page": "https://sm.ms/image/D5VpWCKFElUsPcR"
},
"RequestId": "8A84DDCA-96B3-4363-B5DF-524E95A5201A"
}

解析Markdown

正则表达式:

1
aims = re.findall('!\[.*?\]\((.*?)\)', markdown_content, re.M) # re.M 跨行匹配
  • 通过这个表达式,我们可以将文件中的所有图片路径解析出来,然后再针对每个路径计算它的绝对路径传递给post_img函数上传图床。

绝对路径获取:

1
2
3
4
5
6
7
8
9
def get_path(rt, rel):
return os.path.abspath(rt+rel)

_user_path = os.path.expanduser('~') # 用户根目录
rt_path = dir_char.join(os.path.abspath(filePath).split(dir_char)[:-1]) + dir_char # MarkDown所在文件夹
for aim in aims:
raw_path = aim
aim = aim.replace('~', _user_path)
aim = aim if aim.startswith(dir_char) else get_path(rt_path, aim)
  • 此时,aim值为绝对路径

上传、替换与结果显示

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
def format_markdown(filePath):
_user_path = os.path.expanduser('~')
rt_path = dir_char.join(os.path.abspath(filePath).split(dir_char)[:-1]) + dir_char
tb = PrettyTable()
tb.field_names = ['File', 'Status', 'url']
img_set = {}
with open(filePath, 'r') as fp:
ct = fp.read()
aims = re.findall('!\[.*?\]\((.*?)\)', ct, re.M)
for aim in aims:
raw_path = aim
aim = aim.replace('~', _user_path)
aim = aim if aim.startswith(dir_char) else get_path(rt_path, aim)
if aim not in img_set:
res = post_img(aim)
if not res:
tb.add_row([aim.split(dir_char)[-1], 'No File', ''])
img_set[aim] = False
else:
tb.add_row([aim.split(dir_char)[-1], res['success'], '' if not res['success'] else res['data']['url']])
img_set[aim] = res['data']['url']
if img_set[aim]:
ct = ct.replace(raw_path, img_set[aim])
with open(filePath, 'w') as fp:
fp.write(ct)
print(tb)

全部代码

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import requests
import json
import sys
import re
import os
from prettytable import PrettyTable

if sys.platform.startswith('win'):
dir_char = '\\'
else:
dir_char = '/'


def post_img(filePath):
headers = {
'Authorization': 'MEQCiM6viKEEF8RYPRIpOL4DA7oGtwvA',
}
try:
data = {
'smfile': (filePath.split('/')[-1], open(filePath, 'rb')),
'format': 'json'
}
except:
return False
res = requests.post('https://sm.ms/api/v2/upload', headers=headers, files=data).text
return json.loads(res)


def get_path(rt, rel):
return os.path.abspath(rt+rel)


def format_markdown(filePath):
_user_path = os.path.expanduser('~')
rt_path = dir_char.join(os.path.abspath(filePath).split(dir_char)[:-1]) + dir_char
tb = PrettyTable()
tb.field_names = ['File', 'Status', 'url']
img_set = {}
with open(filePath, 'r') as fp:
ct = fp.read()
aims = re.findall('!\[.*?\]\((.*?)\)', ct, re.M)
for aim in aims:
raw_path = aim
aim = aim.replace('~', _user_path)
aim = aim if aim.startswith(dir_char) else get_path(rt_path, aim)
if aim not in img_set:
res = post_img(aim)
if not res:
tb.add_row([aim.split(dir_char)[-1], 'No File', ''])
img_set[aim] = False
else:
tb.add_row([aim.split(dir_char)[-1], res['success'], '' if not res['success'] else res['data']['url']])
img_set[aim] = res['data']['url']
if img_set[aim]:
ct = ct.replace(raw_path, img_set[aim])
with open(filePath, 'w') as fp:
fp.write(ct)
print(tb)

if __name__ == '__main__':
format_markdown(sys.argv[1])

调用:

1
python3 ImgBed.py [Markdown file path]