MENU

尝试写一个Python程序

2019 年 09 月 26 日 • 派森编程

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日两天我并没有新增删除或更新我的任何静态文件,但是备份策略被触发了,在控制台看这种结果,

莫名其妙,文件大小还不一样,准备彻查一下。

最后编辑于: 2019 年 10 月 09 日
返回文章列表 文章二维码 打赏
本页链接的二维码
打赏二维码