尝试写一个Python程序
emmmm
,最近一段时间在看python
,终于是将最基础部分看完了,所以打算找个东西来练练手,找到了一个比较合适的,那就是博客备份方面的东西,有两个脚本,第一个脚本每日凌晨零点开始执行备份,也就是备份网站根目录和数据库,都是存的tar
包,存到服务器,备份文件保留前30
天。
第二个脚本是将第一个脚本备份出来的数据库和网站根目录文件再次进行打包,整合成一个压缩包,执行时间会比第一个晚几分钟,然后用qshell
将这个备份文件传到七牛云再做一次备份,现在准备将文件上传七牛云这步由python
脚本来做,之前上传的时候只是单纯的将备份文件传上去了,并没有删除历史文件。
虽然说云存储有设置文件生命周期的配置,但是那个配置是我今天才发现的,而且只能对新上传的文件生效,之前都是一直在手动删除一个月之前的备份文件,想起来了就删一下,所以现在改了下第一个脚本,将整合的功能加到了第一个脚本里面,所以第一个脚本执行后会有三个文件,分别是数据库备份文件、网站根目录备份文件,还有要传到七牛云存储的文件,要上传至云存储的位置文件名如下,
[root@rj-bai ~]# ls /backup/qiniu-blog/
blog.rj-bai.com-2019-09-25.tar.gz
blog.rj-bai.com-`date +%Y-%m-%d "-d -1 days"`.tar.gz
而我上传到七牛云的文件名如下,
就是因为之前在脚本里写错了,
qshell rput blog-backup blog.rj-bai.com-`date +%Y-%m-%d "-d -1 days"`-tar.gz blog.rj-bai.com-`date +%Y-%m-%d "-d -1 days"`.tar.gz
所以这里准备一直错下去了,所以我给自己提点了个需求,第一将今天的备份文件上传至七牛云存储指定bucket
内,第二删除30
天以前的文件,就这么简单的功能,七牛官方Python SDK
地址,所以我也是按着官方示例来的,自己也加了点东西。
至于上面备份脚本做的事情我不打算用python
去写了,其实可以用os.system()
去做,感觉太麻烦了,所以基于pycharm + python3.7
进行开发,代码如下
练手第一版
from os import path
from datetime import datetime, timedelta
from qiniu import Auth, put_file, etag, build_batch_delete, BucketManager
# 需要填写你的 Access Key 和 Secret Key
access_key = 'access_key'
secret_key = 'secret_key'
# 构建鉴权对象
auth = Auth(access_key, secret_key)
# 要操作的bucket名字
bucket_name = 'bucket_name'
# 获取文件列表认证信息
bucket = BucketManager(auth)
prefix = None
limit = 1000
marker = None
delimiter = None
# 定义获取昨天日期的函数,只保留了年月日且转为字符串
def get_yesterday():
today = datetime.now()
oneday = timedelta(days=1)
yesterday_str = today - oneday
yesterday = datetime.strftime(yesterday_str, '%Y-%m-%d')
return yesterday
# 获取指定时间内备份文件的文件名,保留这些文件名到列表中,后期基于这个列表进行判断
def get_before_date(day):
agate_list = []
i = 0
while i < day:
today_date = datetime.today()
yesterday_date = timedelta(days=i)
beforedate = today_date - yesterday_date
agodate = beforedate.strftime(str('%Y-%m-%d')).split(" ")
i += 1
for file_name in agodate:
agate_list.append('blog.rj-bai.com-{}-tar.gz'.format(file_name))
return agate_list
# 获取目前存在的备份文件列表
def get_backup_file():
ret, eof, info = bucket.list(bucket_name, prefix, marker, limit, delimiter)
# 开始处理返回数据,返回的是一个字典,只需获取items对应的值即可,处理后返回的是一个大列表,列表里包含字典
file_source_list = ret['items']
# 定义空列表字典
file_dict = {}
file_list = []
# 开始处理上面截取后的列表
for i in range(0, len(file_source_list)):
# 将列表元素添加至上面的空字典,有则更新无则添加
file_dict.update(file_source_list[i])
# 这时字典带有列表第一个元素,只需获取到key的值,也就是文件名添加到空列表中,循环过后所有现有备份文件名都会存到file_list列表中
file_list.append(file_dict['key'])
return file_list
# 定义备份文件信息
BACKUP_FILE = '/backup/qiniu-blog/blog.rj-bai.com-{}.tar.gz'.format(get_yesterday())
# 上传后保存的文件名
key = 'blog.rj-bai.com-{}-tar.gz'.format(get_yesterday())
# 生成上传Token,可以指定过期时间等
token = auth.upload_token(bucket_name, key, 3600)
# 开始判断备份文件是否存在,如果不存在直接退出程序
if not path.exists(BACKUP_FILE):
exit(1)
else:
# 如果备份文件存在则开始上传
ret, info = put_file(token, key, BACKUP_FILE)
assert ret['key'] == key
assert ret['hash'] == etag(BACKUP_FILE)
# 开始批量删除备份文件,保留最近30天的
# 定义空列表,用于存放要被删除的文件列表
del_file_list = []
# 开始循环get_backup_file函数返回值,也就是当前云存储上有哪些文件
for i in range(0, len(get_backup_file())):
# 定义一个新变量用于存储get_backup_file每一个值
del_file = get_backup_file()[i]
# 拿到值后开始调用get_before_date函数生成前30天的文件名,然后判断云存储文件名是否在get_before_date生成的文件名内
if del_file not in get_before_date(30):
# 如果不存在将该文件名存入del_file列表
del_file_list.append(del_file)
# 最后删除列表内文件
ops = build_batch_delete(bucket_name, del_file_list)
ret, info = bucket.batch(ops)
判断文件是什么时候上传的我只能通过文件名去判断,暂时没别的办法,如果文件名出现格式上的错误也会被删除,我并没有打印任何提示信息,所以我先在扔到我的服务器上执行一下试试就行了,需要先安装一下qiniu
包,
[root@rj-bai ~]# pip3.7 install qiniu
安装完就可以执行了,看上面目前云存储有31
个文件,执行完的结果现在算一下,
>>> from datetime import timedelta, datetime
>>> today = datetime.today()
>>> threeday_age = timedelta(days=30)
>>> print(today - threeday_age)
2019-08-27 16:43:28.612440
也就是说08-27
号及之前的文件都被删掉了,云存储最后一个包名时间是2019-09-25-tar.gz
就对了,
[root@rj-bai ~]# python3.7 /python_script/rsync_qiniu.py
执行完后去云存储看看,
看上去是没问题,但是我保留的是30
天的,为毛只剩29
个文件了,问题就出在了get_before_date
函数里,他将今天的期日也算进去了,所以这算是一个BUG
,解决办法就是将get_before_date
函数内计数器值改为1
,传实参的时候多写一天就可以了撒,再或者<
改<=
,大概就是这样,接下来吧计划任务调一下就完事了。
所以我人生中第一个能派上用场的python
程序就此诞生,虽然只是实现了一个比较简单的小功能,测了一下python 2.7
执行也莫得问题,溜了溜了。
下文是后期加的,其实我还有一个bucket
是专门用来存博客静态资源文件的,之前用阿里云OSS
的时候误删过OSS
文件,如果删掉了就找不回来了,没有什么类似回收站的东西,七牛的也如此,所以在很久之前我就将这个备份做了,以防自己手贱又误删。
因为这个静态文件不是每天都会新增或更新,当前的备份逻辑是每天的某个时间使用qshell
拉取全部的静态文件存到服务器本地,拉完之后会判断静态文件数量,如果相比之前一天的多了则对文件夹进行打包,打包后传到指定的bucket
内,如果静态文件数与前一天一致拉取文件后不进行任何操作,历史文件保留30
天,所以我准备用一个python
脚本对这两个bucket
进行上传删除历史文件操作,至于拉文件判断有没有变化还是用shell
脚本就够了,在写的时候发现了一个蛋疼的问题,如下,
文件后缀不一致,一个是.
,一个是-
,后缀不统一写起来就有点麻烦了,所以先把所有文件的后缀统一一下,用.tar.gz
,所以blog
的都得改名,之前还说要一直错下去,现在看来不行了,两种方式改名,一个是手动去控制台改名,再就是用SDK
,我这里准备用SDK
去改了,看官方的文档就是需要用字典,key
为源文件名,value
为新名字,也就是这种格式
{'blog.rj-bai.com-2019-09-08-tar.gz':'blog.rj-bai.com-2019-09-08.tar.gz'}
这个就很简单了,获取文件列表的函数之前写好了,定义一个空字典key
为源文件名,value
还是原文件名,配合replace
函数替换一下,将-tar
替换为.tar
即可,所以改名代码如下,获取当前存在的备份文件列表的函数改了,
from qiniu import build_batch_rename, Auth, BucketManager
# 需要填写你的 Access Key 和 Secret Key
access_key = 'your_access'
secret_key = 'your_secret'
# 构建鉴权对象
auth = Auth(access_key, secret_key)
# 要操作的bucket名字,一共是两个
bucket_name = 'blog-backup'
# 获取文件列表认证信息
bucket = BucketManager(auth)
prefix = None
limit = 1000
marker = None
delimiter = None
# 获取目前存在的备份文件列表,根据不同的buckets去返回对应的值
def get_backup_file(buckets_name):
ret, eof, info = bucket.list(buckets_name, prefix, marker, limit, delimiter)
# 开始处理返回数据,返回的是一个字典,只需获取items对应的值即可,处理后返回的是一个大列表,列表里包含字典
file_source_list = ret['items']
# 定义空列表字典
file_dict = {}
file_list = []
# 开始处理上面截取后的列表
for i in range(0, len(file_source_list)):
# 将列表元素添加至上面的空字典,有则更新无则添加
file_dict.update(file_source_list[i])
# 这时字典带有列表第一个元素,只需获取到key的值,也就是文件名添加到空列表中,循环过后所有现有备份文件名都会存到file_list列表中
file_list.append(file_dict['key'])
return file_list
# 空字典撒
rename_dict = {}
# 开始循环撒
for i in range(0, len(get_backup_file(bucket_name))):
file_value = get_backup_file(bucket_name)[i]
# 对空字典进行操作撒,替换一下字符串
rename = {file_value : '{}'.format(file_value).replace("-tar", ".tar")}
rename_dict.update(rename)
# 开始改名撒
ops = build_batch_rename(bucket_name, rename_dict, force='true')
ret, info = bucket.batch(ops)
跑完了撒再看一下文件名,
这样就可以了撒,然后对之前的脚本大改,N
多地方都要根据bucket
获取文件名,或者根据文件名判断是哪个bucket
里面的,emmmm
,所以最终的如下,
练手第二版
from os import path
from datetime import datetime, timedelta
from qiniu import Auth, put_file, etag, build_batch_delete, BucketManager
# 需要填写你的 Access Key 和 Secret Key
access_key = 'your_access'
secret_key = 'your_secret'
# 构建鉴权对象
auth = Auth(access_key, secret_key)
# 要操作的bucket名字,一共是两个
bucket_name = 'blog-backup&res-backup'.split("&")
# 获取文件列表认证信息
bucket = BucketManager(auth)
prefix = None
limit = 1000
marker = None
delimiter = None
# 定义获取昨天日期的函数,只保留了年月日且转为字符串
def get_yesterday():
today = datetime.now()
oneday = timedelta(days=1)
yesterday_str = today - oneday
yesterday = datetime.strftime(yesterday_str, '%Y-%m-%d')
return yesterday
# 获取指定时间内备份文件的文件名,保留这些文件名到列表中,后期基于这个列表进行判断,根据不同的buckets返回不同的值
def get_before_date(day, buckets_name):
agate_list = []
i = 1
while i < day:
today_date = datetime.today()
yesterday_date = timedelta(days=i)
beforedate = today_date - yesterday_date
agodate = beforedate.strftime(str('%Y-%m-%d')).split(" ")
i += 1
if buckets_name == bucket_name[0]:
for file_name in agodate:
agate_list.append('blog.rj-bai.com-{}.tar.gz'.format(file_name))
else:
for file_name in agodate:
agate_list.append('res.rj-bai.com-{}.tar.gz'.format(file_name))
return agate_list
# 获取目前存在的备份文件列表,根据不同的buckets去返回对应的值
def get_backup_file(buckets_name):
ret, eof, info = bucket.list(buckets_name, prefix, marker, limit, delimiter)
# 开始处理返回数据,返回的是一个字典,只需获取items对应的值即可,处理后返回的是一个大列表,列表里包含字典
file_source_list = ret['items']
# 定义空列表字典
file_dict = {}
file_list = []
# 开始处理上面截取后的列表
for i in range(0, len(file_source_list)):
# 将列表元素添加至上面的空字典,有则更新无则添加
file_dict.update(file_source_list[i])
# 这时字典带有列表第一个元素,只需获取到key的值,也就是文件名添加到空列表中,循环过后所有现有备份文件名都会存到file_list列表中
file_list.append(file_dict['key'])
return file_list
# 定义备份文件信息
RSYNC_QINIU_FILE = '/backup/qiniu-blog/{0}.rj-bai.com-{2}.tar.gz&/backup/qiniu/{1}.rj-bai.com-{2}.tar.gz'.format('blog', 'res', get_yesterday()).split("&")
# 生成token,根据不同的文件名获取不同的token
def get_token(file_name):
if file_name.find('res'):
FILE_NAME = path.basename(RSYNC_QINIU_FILE[0])
token = auth.upload_token(bucket_name[0], FILE_NAME, 3600)
return str(token)
else:
FILE_NAME = str(path.basename(RSYNC_QINIU_FILE[1]))
token = auth.upload_token(bucket_name[1], FILE_NAME, 3600)
return str(token)
# 开始备份文件,如果文件不存在则跳出这次循环,程序不退出
for i in range(0, len(RSYNC_QINIU_FILE)):
if not path.exists(RSYNC_QINIU_FILE[i]):
continue
else:
FILE_NAME = str(path.basename(RSYNC_QINIU_FILE[i]))
ret, info = put_file(get_token(file_name=FILE_NAME), FILE_NAME, RSYNC_QINIU_FILE[i])
assert ret['key'] == FILE_NAME
assert ret['hash'] == etag(RSYNC_QINIU_FILE[i])
# 开始批量删除备份文件,保留最近30天的
# 定义两个空列表,用于存放要被删除的文件列表,有两个bucket
res_rm_file = []
blog_rm_file = []
for i in (bucket_name):
if not i.find("res"):
for res_file in range(0, len(get_backup_file(i))):
res_del_file = get_backup_file(i)[res_file]
if res_del_file not in get_before_date(31, i):
res_rm_file.append(res_del_file)
else:
for blog_file in range(0, len(get_backup_file(i))):
del_blog_file = get_backup_file(i)[blog_file]
if del_blog_file not in get_before_date(31, i):
blog_rm_file.append(del_blog_file)
# 删除两个列表内的文件
for i in res_rm_file, blog_rm_file:
if 'blog' in str(i):
ops = build_batch_delete(bucket_name[0], blog_rm_file)
ret, info = bucket.batch(ops)
else:
ops = build_batch_delete(bucket_name[1], res_rm_file)
ret, info = bucket.batch(ops)
经过测试是没什么问题,能优化的地方太多了,懒得搞了,准备继续向下看了,还有一件事要查,那就是本月4/5
日两天我并没有新增删除或更新我的任何静态文件,但是备份策略被触发了,在控制台看这种结果,
莫名其妙,文件大小还不一样,准备彻查一下。
练手第三版
import re
import copy
from os import path
from datetime import datetime, timedelta
from qiniu import Auth, put_file, etag, build_batch_delete, BucketManager
class File_backup(object):
def __init__(self, file_path):
# 要备份的文件名和文件路径
self.file_path = file_path
self.file_name = path.basename(self.file_path)
# 需要填写你的 Access Key 和 Secret Key
self.access_key = 'Access Key'
self.secret_key = 'Secret Key'
# 构建鉴权对象
self.auth = Auth(self.access_key, self.secret_key)
# 获取文件列表认证信息
self.bucket = BucketManager(self.auth)
self.prefix = None
self.limit = 1000
self.marker = None
self.delimiter = None
# 获取当前时间进行格式化
self.now_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 存放指定时间内备份的文件名
self.Time_range_file = []
# 存放目前存在的备份文件列表
self.Backup_file_exists = copy.deepcopy(self.Time_range_file)
# 定义一个空列表,用于存放要被删除的文件
self.Delete_file_list = copy.deepcopy(self.Time_range_file)
# 判断文件是哪个Bucket
if self.file_name.find('res') != -1:
self.bucket_name = 'res-backup'
else:
self.bucket_name = 'blog-backup'
# 定义token
self.token = self.auth.upload_token(self.bucket_name, self.file_name, 3600)
# 获取昨天日期的函数,静态方法
@staticmethod
def get_yesterday():
today = datetime.now()
one_day = timedelta(days=1)
yesterday_str = today - one_day
yesterday = datetime.strftime(yesterday_str, '%Y-%m-%d')
return yesterday
# 简单记录备份是否备份成功
def write_record(self, mode):
with open('rsync.log', 'a', encoding='utf-8') as log:
if mode == 'File_Not_exists':
rest = '{0} {1} 不存在,跳过'.format(self.now_time, self.file_path)
log.write(rest)
log.write('\n')
if mode == 'File_exists':
rest = '{0} {1} 存在,开始备份'.format(self.now_time, self.file_path)
log.write(rest)
log.write('\n')
if mode == 'Success':
rest = '{0} {1} 上传至七牛云成功'.format(self.now_time, self.file_path)
log.write(rest)
log.write('\n')
if mode == 'Failure':
rest = '{0} {1} 上传至七牛云失败'.format(self.now_time, self.file_path)
log.write(rest)
log.write('\n')
# 删除历史文件方法
def Delete_history_file(self, day):
ret, eof, info = self.bucket.list(self.bucket_name, self.prefix, self.marker, self.limit, self.delimiter)
# 开始处理返回数据,返回的是一个字典,只需获取items对应的值即可,处理后返回的是一个大列表,列表里包含字典
file_source_list = ret['items']
# 定义空列表字典
file_dict = {}
# 开始处理上面截取后的列表
for file in range(0, len(file_source_list)):
# 将列表元素添加至上面的空字典,有则更新无则添加
file_dict.update(file_source_list[file])
# 这时字典带有列表第一个元素,只需获取到key的值,也就是文件名添加到空列表中,循环过后所有现有备份文件名都会存到file_list列表中
self.Backup_file_exists.append(file_dict['key'])
# 根据传入实参生成N天内的文件名
counter = 1
while counter <= day:
today_date = datetime.today()
yesterday_date = timedelta(days=counter)
before_date = today_date - yesterday_date
ago_date = before_date.strftime('%Y-%m-%d')
counter += 1
# 定义正则查找日期
regex = re.compile(r'(\d{4}).+(\d{2}).+(\d{2})')
# 正则替换字符串
rest_pos = (regex.sub(ago_date, self.file_name))
self.Time_range_file.append(rest_pos)
# 开始判断现存备份文件是否在指定时间范围内,如果不在将文件名添加到删除列表
for j in self.Backup_file_exists:
if j not in self.Time_range_file:
self.Delete_file_list.append(j)
# 如果列表不为空则删除列表内文件
if self.Delete_file_list:
ops = build_batch_delete(self.bucket_name, self.Delete_file_list)
self.bucket.batch(ops)
# 上传文件方法
def Upload_files(self):
if not path.exists(self.file_path):
self.write_record(mode='File_Not_exists')
else:
try:
ret, info = put_file(self.token, self.file_name, self.file_path)
assert ret['key'] == self.file_name
assert ret['hash'] == etag(self.file_path)
self.write_record(mode='Success')
except:
self.write_record(mode='Failure')
if __name__ == '__main__':
# 要备份的文件
BACKUP_FILE = '/backup/qiniu-blog/{0}.rj-bai.com-{2}.tar.gz&/backup/qiniu/{1}.rj-bai.com-{2}.tar.gz'\
.format('blog', 'res', File_backup.get_yesterday()).split("&")
# 开始调用类方法
for i in BACKUP_FILE:
bak = File_backup(file_path=i)
# 上传文件
bak.Upload_files()
# 删除历史文件
bak.Delete_history_file(day=30)
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。