图床批量下载and更换
python脚本 批量下载markdown文件图片及批量替换到 SM.MS 图床.
更新
1
19.03.28 初始
- 参考资料
https://sm.ms/doc/
https://pypi.org/project/smms.py/0.1.1/#description
导语
- 收到邮件,使用了很久的图床突然要在4月底关闭.只能优先处理图床的问题,数据结构暂时延后.
- 很久就像整理一遍图片链接了,早期的博文N多的图片已经不能正常访问了,正好由此机会,可惜,又是个大坑…
需求
需求:
- 新图床:
- 支持https,运营商很厉害的…
- 最好已经运行很久,经常换伤不起…
- 支持管理/删除等.有时会上传错误…
- 下载
- 分文件,下载全部图片
- 处理图片名称不规范问题(越早的博文,越不规范…例如:压根没写文件名…)
- 上传
- 上传图片(废话)
- 自行替换对应旧链接.
- 不影响已下载图片.
- 新图床:
新图床:
- 新浪微博,自带国内cdn,支持https,
- 有时莫名其妙会删图片,有时n久之前的图片莫名挂了.
- 微博会自动加一层看不见的水印.
- 可以反查微博帐号
- 简书,七牛,腾讯云等.
- 有上手难度.
- 图片可能会挂
- SM.MS
- 支持https
- 服务器在香港,速度非常好.
- 已经运行了很久,没有突发情况,推论不会出现关闭.
- 微软onedrive自建
- 数据全在自己的onedrive,管理非常方便
- 上手还是有点难度,研究中.
- 微软一时半会还不会砍掉onedrive.
- 最后选择 SM.MS 作为过渡,看看还有没有其他的解决方案.其中的坑在对应代码有说明.
- 新浪微博,自带国内cdn,支持https,
下载
- 因为早期使用的图床年久失修,链接非常困难,必须要走本地代理.
- python 内置的 urllib 库 request.urlretrieve,下载非常方便
- 博文中N多图片链接连图片名都没有,或者有图片名没后缀,都需要处理一下.
- 只能求助于正则来处理,最后实在不行使用url的文件名替换.(正则真是坑了一把…)
- 因为早期使用的图床年久失修,链接非常困难,必须要走本地代理.
上传
- SM.MS的API文档很全,成功上传问题不大.
- 换第三方的 requests 库,也有代码参考.
- 但是不支持中文图片名.
- 临时重命名,最重要的是取得图片的链接.
- SM.MS的API文档很全,成功上传问题不大.
api
- 这里是把上传/下载的共用函数抽出来放在 file_api 和 image_api 两个文件里了.
file_api
图片名不全的依照下面的规则
- 有后缀?
- 没有后缀添加 .png
- 为空字符,图片名替换为 url 中的图片名.
代码注释很全,有问题请留言或邮件.
代码
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
62#!/usr/bin/python3
import os
import re
# 返回本目录下 .md 文件
def get_dir_md():
arr = [
x for x in os.listdir('.')
if os.path.isfile(x) and os.path.splitext(x)[1] == '.md'
]
return arr
# 文件名含有中文?
def is_fname_zh(filename):
# 比较每一个字符的编码是否在 unicode 分配的中日韩文编码区
return any('\u4e00' <= char <= '\u9fff' for char in filename)
# 返回url中图片名
def get_urlname(url):
return re.search(r'.*\w\/(.*)$', url).group(1)
# 返回md文件 list [图片名称,图片链接]
def get_urlist(flie):
with open(flie, 'r') as f:
tmp = re.findall(r'.*!\[(.*?)]\((.*?)\).*', f.read())
urlist = []
name = set()
for t in tmp:
u = list(t)
# 如果图片名不全(没有后缀..当时写的不规范),
if not re.match(r'.*[.].*', u[0]):
# 图片名为空
if u[0] == '':
# 取url的图片名
u[0] = re.search(r'.*\w\/(.*)$', u[1]).group(1)
# 没有后缀添加后缀(大部分为png)
else:
u[0] += '.png'
# 如果图片名重名
if u[0] in name:
# 取url的文件名
u[0] = re.search(r'.*\w\/(.*)$', u[1]).group(1)
print('rename %s' % (u[0]))
name.add(u[0])
urlist.append(u)
return urlist
# 替换md文件中链接
def re_md_url(flie, urlist):
with open(flie, 'r') as f:
tmp = f.read()
for ulist in urlist:
tmp = tmp.replace(ulist[1], ulist[2])
with open(flie, 'w') as f:
f.write(tmp)
image_api
SM.MS上传代码参考
https://pypi.org/project/smms.py/0.1.1/#description
urllib 使用本地代理
https://stackoverflow.com/questions/22967084/urllib-request-urlretrieve-with-proxy
代码注释很全,有问题请留言或邮件.
代码
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#!/usr/bin/python3
import os
import requests
import json
from glob import glob
from urllib import request
params = {'format': 'json', 'ssl': True}
prefix = 'https://sm.ms/api'
# 上传图片
def api_upload(image):
'''upload an image file'''
files = {'smfile': open(image, 'rb')}
print()
resp = requests.post(
'{}/upload'.format(prefix), files=files, params=params)
return resp.text
def upload(filename):
images = glob(filename)
if not images:
print('%s not image' % (filename))
for image in images:
# upload an image
resp = json.loads(api_upload(image))
# 返回上传图片的url
if resp['code'] == 'error':
# 打印错误信息
print('uploda error msg:%s' % (resp['msg'].lower()))
return None
else:
print('%s upload success' % (filename))
return resp['data']['url']
# 下载图片
def down(file, url):
if not os.path.exists(file):
print('down %s' % (file))
# 本地http代理
proxy = request.ProxyHandler({'http': '127.0.0.1:999'})
opener = request.build_opener(proxy)
request.install_opener(opener)
request.urlretrieve(url, file)
else:
# 文件已存在
print('%s is exist' % (file))
下载
新建一个 .md 文件名的文件夹,下载图片到对应文件夹,对图片名的处理见flie一节.
代码
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#!/usr/bin/python3
import os
from image_api import down
from flie_api import get_dir_md, get_urlist
if __name__ == "__main__":
# 获取 .md 文件
arr = get_dir_md()
for md in arr:
# 获取urllist
urlist = get_urlist(md)
if urlist:
# 创建目录
fdir = os.path.splitext(md)[0]
try:
os.mkdir(fdir)
except FileExistsError as e:
e
# 进入子目录
os.chdir(fdir)
print('enter %s' % (os.path.abspath('.')))
for u in urlist:
down(u[0], u[1])
print('leave %s' % (os.path.abspath('.')))
# 返回上级目录
os.chdir('../')
else:
# urlist 为空
continue
上传
上传到 SM.MS 图床,中文图片名,暂时重命名文件.
图床有单ip的一定时间的上传的次数限制,因此要处理所有博文,加了延迟,上传间隔20s,处理完一个文件延迟60s.测试还好,就是最后一个文件时,遇到了限制…
文件数量有限可以干掉延时.
代码
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#!/usr/bin/python3
import os
import time
from image_api import upload
from flie_api import get_dir_md, get_urlist, is_fname_zh, get_urlname, re_md_url
if __name__ == "__main__":
# 获取.md文件
arr = get_dir_md()
for file in arr:
# 获取urlist
urlist = get_urlist(file)
if urlist:
# 进入子目录
fdir = os.path.splitext(file)[0]
os.chdir(fdir)
print('enter %s' % (os.path.abspath('.')))
# 上传图片
for u in urlist:
# u[0]图片名含有中文,临时重命名
if is_fname_zh(u[0]):
tmp = get_urlname(u[1])
os.rename(u[0], tmp)
else:
tmp = u[0]
# 上传图片
u.append(upload(tmp))
os.rename(tmp, u[0])
# 延时
time.sleep(20)
# 返回上级目录
print('leave %s' % (os.path.abspath('.')))
os.chdir('../')
# 替换链接
re_md_url(file, urlist)
# 延时
time.sleep(60)
else:
# urlist 为空
continue
结语
- 终于处理完图床的问题了,接下来回到数据结构,被正则坑了好久…