为知笔记收费了,把笔记迁移到 leanote 上,但是有担心笔记的安全,所以找到了一个备份脚本,每天备份日志到本地。
备份脚本
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403cat Leanote4MD.py#!/usr/bin/env python#encoding: utf8## author: goodbest <lovegoodbest@gmail.com># github: github.com/goodbestimport requestsimport jsonimport osimport sysfrom datetime import datetimeimport dateutil.parserfrom dateutil import tzfrom PIL import Imagefrom StringIO import StringIOfrom requests_toolbelt import SSLAdapterimport sslimport argparseimport ConfigParser as CPleanote_host = Noneleanote_token = Nonelocal_zone = Noneargs = Noneconfigpath = "Leanote.cfg"DEBUG = 0def is_ok(myjson):try:json_object = json.loads(myjson)except ValueError, e:print ereturn Falseif 'Ok' in json_object:if json_object['Ok']:return Trueelse:print json_object['Msg']return Falseelse:return Truedef req_get(url, param = '', type = 'json', token = True):if token:if param:param.update({'token': leanote_token})else:param={'token': leanote_token}s = requests.Session()if leanote_host.startswith('https'):s.mount('https://', SSLAdapter(ssl.PROTOCOL_TLSv1))r = s.get(leanote_host + '/api/' + url, params = param)if r.status_code == requests.codes.ok:if type=='json':if is_ok(r.text):rj = json.loads(r.text)# if 'Msg' in rj:# rj=rj['Msg']return rjelse:print '[Err] requests to url %s fail' %(r.url)return Noneelif type=='image':i = Image.open(StringIO(r.content))return ielse:print '[Err] connect to url %s fail, error code %d ' %(r.url, r.status_cde)return Nonedef req_post(url, param = '', type = 'json', token = True):if token:if param:param.update({'token': leanote_token})else:param={'token': leanote_token}s = requests.Session()if leanote_host.startswith('https'):s.mount('https://', SSLAdapter(ssl.PROTOCOL_TLSv1))r = s.post(leanote_host + '/api/' + url, data = param)if r.status_code == requests.codes.ok:if type=='json':if is_ok(r.text):rj = json.loads(r.text)# if 'Msg' in rj:# rj=rj['Msg']return rjelse:print '[Err] requests to url %s fail' %(r.url)return Noneelse:print '[Err] connect to url %s fail, error code %d ' %(r.url, r.status_cde)return None#ret leanote_tokendef login(email, pwd):param = {'email': email,'pwd': pwd,}r = req_get('auth/login', param, token=False)if r:print 'Login success! Welcome %s (%s)' %(r['Username'], r['Email'])return r['Token']else:print 'Login fail! Start again.'exit()def logout():return req_get('auth/logout')#ret dict(notebookId: type.Notebook}def getNotebooks(includeTrash = False):r = req_get('notebook/getNotebooks')if r:if includeTrash:return {notebook['NotebookId'] : notebook for notebook in r}else:return {notebook['NotebookId'] : notebook for notebook in r if not notebook['IsDeleted']}else:return#ret [type.Note], which contains noteId, and note meta datadef getNotesMeta(notebookId):param = {'notebookId': notebookId,}return req_get('note/getNotes', param)#ret type.NoteContentdef getNoteDetail(noteId):param = {'noteId': noteId,}return req_get('note/getNoteAndContent', param)def getImage(fileId):param = {'fileId': fileId,}return req_get('file/getImage', param, type = 'image')def addNotebook(title='Import', parentId='', seq=-1):param = {'title': title,'parentNotebookId': parentId,'seq' : seq}return req_post('notebook/addNotebook', param)def addNote(NotebookId, Title, Content, Tags=[], IsMarkdown = True, Abstract= '', Files=[]):param = {'NotebookId': NotebookId,'Title': Title,'Content': Content,'Tags[]': Tags,'IsMarkdown': IsMarkdown,'Abstract': Abstract,#'Files' : seq}return req_post('note/addNote', param)def readFromFile(filename):import yamlfile_meta = ''file_content = ''with open (filename) as f:meta_flag=Falsefor line in f:#print lineif meta_flag:file_content += lineelse:if line.find('---')>-1:meta_flag = Trueelse:file_meta += lineif not meta_flag:file_content = file_metafile_meta = ''if meta_flag:meta = yaml.load(file_meta)else:meta = {}return file_content, metadef saveToFile(notes, noteBooks, path = '.'):unique_noteTitle = set()for note in notes:if note['Title'] == '':filename = note['NoteId']else:filename = note['Title']if filename in unique_noteTitle:filename='%s_%s' %(filename, note['NoteId'])else:unique_noteTitle.add(filename)if note['IsMarkdown']:filename += '.md'else:filename += '.txt'try:with open(path + '/' + filename, 'w') as file:print 'write file: %s' %filenamefile.write('title: %s\n' %note['Title'].encode('utf-8'))date = dateutil.parser.parse(note['CreatedTime'])file.write('date: %s\n' %datetime.strftime(date.astimezone(local_zone), '%Y/%m/%d %H:%M:%S'))date = dateutil.parser.parse(note['UpdatedTime'])file.write('updated: %s\n' %datetime.strftime(date.astimezone(local_zone), '%Y/%m/%d %H:%M:%S'))if note['Tags']:if len(note['Tags']) == 1:if note['Tags'][0]:file.write('tags:\n')for tag in note['Tags']:file.write('- %s\n' %tag.encode('utf-8'))category = []current_notebook = note['NotebookId']category.append(noteBooks[current_notebook]['Title'])while noteBooks[current_notebook]['ParentNotebookId'] != '':category.append(noteBooks[noteBooks[current_notebook]['ParentNotebookId']]['Title'])current_notebook = noteBooks[current_notebook]['ParentNotebookId']file.write('categories:\n')category.reverse()for cat in category:file.write('- %s\n' %cat.encode('utf-8'))file.write('---\n')file.write('%s' %note['Content'].encode('utf-8'))file.close()if note['Files']:if len(note['Files']) > 0:for attach in note['Files']:if not attach['IsAttach']:i = getImage(attach['FileId'])print 'saving its image: %s.%s' %(attach['FileId'], i.format)i.save(attach['FileId'] + '.' + i.format)except:print "error: ", filenamedef LeanoteExportToMD(path = '.'):print 'Reading your notebooks...'noteBooks = getNotebooks()#get not deleted notes listnotes=[]for notebook in noteBooks.values():if not notebook['IsDeleted']:notesMeta = getNotesMeta(notebook['NotebookId'])for noteMeta in notesMeta:if not noteMeta['IsTrash']:note = getNoteDetail(noteMeta['NoteId'])notes.append(note)print 'found %d notes' %len(notes)#write filesaveToFile(notes, noteBooks, path = path)print 'all done, bye~'def LeanoteImportFromMD(path='.'):# filelist = os.listdir(path)# filelist = [file for file in filelist if file.find('.md')>-1 or file.find('.txt')>-1]filelist = readfiles(path)importedNotebookTitleMapID = {}ret = addNotebook(title='imported_note', parentId='', seq=-1)if ret:print 'imporing into a new notebook: %s' %ret['Title']importedNotebookTitleMapID['import'] = ret['NotebookId']for filename in filelist:content, meta = readFromFile(path + '/' + filename)if DEBUG:print metaparentTitle='import'currentTitle=''if meta.get('categories'):categories= meta.get('categories')else:categories=['import']for cat in categories:currentTitle=catif currentTitle in importedNotebookTitleMapID.keys():parentTitle=currentTitleelse:ret = addNotebook(title = currentTitle, parentId = importedNotebookTitleMapID[parentTitle])importedNotebookTitleMapID[currentTitle] = ret['NotebookId']parentTitle=currentTitleif not meta.get('title'):meta['title'] = filename.replace('.md','').replace('.txt','')importedNote = addNote(NotebookId=importedNotebookTitleMapID[currentTitle], Title=meta.get('title'), Content=content, Tags=meta.get('tags', []), Abstract='')if importedNote:print 'imported %s' %filenameprint 'all done, bye~'def readfiles(path):assert os.path.exists(path)filelist = [os.path.join(root, f) for root,_,files in os.walk(path) for f in files if f.find('.md')>-1 or f.find('.txt')>-1]assert filelist, "No files fond in %s" % pathreturn filelistdef main():global leanote_hostglobal leanote_tokenglobal local_zoneglobal argsargs = init_options()leanote_host=args.hostleanote_email=args.userleanote_password=args.passwdpath = args.pathprint 'Connecting to %s' %leanote_hostleanote_token = login(leanote_email, leanote_password)local_zone=tz.tzlocal()if args.choice == 'import':LeanoteImportFromMD(path)elif args.choice == 'export':LeanoteExportToMD(path)else:print 'command format: \npython Leanote4MD.py import\npython Leanote4MD.py export'logout()def init_options():parser = argparse.ArgumentParser()parser.add_argument("choice", choices=["import", "export"], help="import or export")parser.add_argument("--host", dest="host", help="host(defalt:http://leanote.com)")parser.add_argument("-u", "--user", dest="user", help="email for login")parser.add_argument("-p", "--passwd", dest="passwd",help="passwd for login")parser.add_argument("--path", dest="path", help="your save path (default is current dir)")args = parser.parse_args()config_args = readconfig()if not args.host:args.host = config_args.get("host")if not args.user:args.user = config_args.get("email")if not args.passwd:args.passwd = config_args.get("passwd")if not args.path:args.path = config_args.get("path")if DEBUG:print "choice:", args.choiceprint "host:", args.hostprint "user:",args.userprint "passwd:",args.passwdprint "path:", args.pathreturn argsdef readconfig():args = {}config = CP.ConfigParser()config.read(configpath)args["host"] = config.get("conn", "host")args["email"] = config.get("conn", "email")args["passwd"] = config.get("conn", "passwd")args["path"] = config.get("conn", "path")return argsif __name__ == '__main__':sys.exit(main())备份脚本说明
1234567891011121314151617181920212223242526272829#leanote导入导出MD工具- 可以把你储存在[Leanote](http://leanote.com)上的笔记、文章都导出成Markdown文件、文本文件- 也可以把你储存在硬盘的Markdown文件、文本文件都导入到[Leanote](http://leanote.com)上去- 目前支持导入导出含YAML格式的meta信息的文件,参照 [hexo](http://hexo.io/docs/front-matter.html) 的文件格式,也就是说文件头部可以有`title` `tags``date` `categoris`等meta信息- 兼容官方网站,以及自建的服务器(基于beta4,以及API 0.1版本)#如何使用- 首先安装Python2版本- 确保机器已经安装 `requests` `Pillow` `PyYaml` `requests_toolbelt` 等模块,如果没装请 `pip install`- 然后在命令行执行`python leanote4MD.py`- 如果报错,应该是你的 python 路径问题,或者缺少某些python module- 根据提示输入域名(默认是http://leanote.com)、用户邮箱、密码- 域名不要忘记加`http://`- 如果是自建服务器,请保证版本不低于 beta4- 记得用邮箱,而不是用户名- 一般导入的错误都是文件没有严格按照YAML格式(多余空格等)造成的解析错误#功能- [x] 从Leanote导入、导出笔记本/子笔记本到MD或txt文本文件- [x] 保存为兼容 hexo front matter 的tag、category、date、title等- 由于0.1版本API限制,导入时暂时无法设置 保存时间、修改时间- [x] 只导入、导出不在垃圾箱的笔记- [x] 数不尽的bug- [ ] 根据是否为已发布的blog,生成post或者draft属性- [x] 导出时保存图片到本地- [ ] 导入时提交图片到服务器备份脚本配置文件,原始备份脚本是交互式的,改为读取配置文件了
|
|
- 备份脚本
|
|
- 增加cron,每天备份一次