Loading... ## 背景 随手记是金蝶出品的一款记账软件,其实还是不错的,不过我从来没有充过会员,一直也长期用着用了有快三年了,已经有快4000条流水了,每天坚持记账也养成了挺好的习惯。不过近期正好在尝试其他的记账APP,所以发现了竟然还有语音记账和自动记账等功能(自动记账利用的是安卓的无障碍功能识别界面内容来触发),试用了一下还挺好用的。因此我就计划迁移出随手记了,迁移到我试用了还不错的`一木记账`App来使用。 但是在迁移的过程中却发现随手记APP的Excel导出是需要会员的,我就导出一下就不用你了竟然收我14块钱一个月的费用,实在太不划算了。 账本的设置-高级功能-备份与同步-导出数据到Excel(CSV)竟然是锁住的,免费版用户导出不了,得充值14块钱一个月才行。 ![请输入图片描述](http://static.fox-9.com/uploads/2024/05/16/微信图片_20240516204928.jpg!webp) 于是到处找解决的方案,最后发现,在设置-高级功能-备份与同步-本地备份与恢复,可以进行免费的导出。导出的文件是kdf后缀的文件,不是常见的文件类型,只有随手记app自己可以导入和解析恢复,那么我们要怎么取到里面的账单内容呢? 一顿好找,最后在吾爱破解找到一个大佬的kdf的解析的文章,其实kdf的本质是一个zip的压缩包里面呢包含了一个sqlite数据库,还有一些其他文件,这个sqlite数据库就是我们需要的账单了。但这个sqlite数据库的文件头是被魔改的,需要改回来才能用。 不过可惜,这位吾爱大佬的文章中给的代码是他自己用的,缺少了非常多的关键信息,因此直接执行是无法执行的,同时我要导入一木记账的代码是并没有的,因此努力修改了一下原始的代码思路,目前已经可以直接使用啦! ## 将随手记kdf解密并生成可阅读的sqlite数据库 首先我们需要从随手记里面“免费”导出kdf账单文件。 在设置-高级功能-备份与同步-本地备份与恢复中导出即可,然后把该文件传输到电脑的某个上,并修改文件名为`record.kdf`方便我们后续的处理。 ![请输入图片描述](http://static.fox-9.com/uploads/2024/05/16/image-20240516211021416.png!webp) 接下来,运行我们的第一个脚本: ```python import zipfile import os def unzip_kbf(input_files=None): zf = zipfile.ZipFile(input_files) zf.extractall(path='./') def ssj_kbf_sqlite_convert(input_file, output_file): """ convert ssj data, after kbf unzip to sqlite,convert it to normal sqlite database file :param input_file: the mymoney.sqlite file path :param output_file: the convert mymoney.sqlite file path :return: """ sqlite_header = (0x53, 0x51, 0x4C, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x20, 0x33, 0x0) if os.path.exists(output_file): os.remove(output_file) with open(input_file, mode='rb') as f: with open(output_file, mode='wb') as fw: data_buffer = f.read() write_buffer = bytearray(data_buffer) index = 0 while index < len(sqlite_header): write_buffer[index] = sqlite_header[index] index = index + 1 fw.write(write_buffer) print("convert done") # 执行kbf文件解密 unzip_kbf("record.kbf") ssj_kbf_sqlite_convert("mymoney.sqlite", "record_decrypt.sqlite") ``` 此时该文件夹即可生成一个已经解密的`record_decrypt.sqlite`文件。 这个文件就是可以直接读取数据的数据了,我们使用一些数据库读取的工具就可以查看里面的内容了,比如navicat等等。其中的t_transaction表就是我们交易的账单了。 当然还有其他的一些表还需要进行联立,我们只需要使用sql的知识把我们需要的字段弄出来即可。 ## 生成导入到一木记账的Excel模板 首先一木记账的模板可以在一木记账APP里面导出来,大概长这样: ![请输入图片描述](http://static.fox-9.com/uploads/2024/05/16/image-20240516211736700.png!webp) 于是我也给你提供了我测试好可以导入的获取的代码,使用代码之前你需要安装一下三方的库: ```bash pip install pandas xlwt ``` 接下来是代码: ```python import sqlite3 import pandas as pd import xlwt # 连接到SQLite数据库,如果数据库文件不存在,会自动在当前目录创建一个 # 数据库文件名为example.db conn = sqlite3.connect('record_decrypt.sqlite') # 创建一个游标对象,用于执行SQL语句 cursor = conn.cursor() sql = '''SELECT strftime('%Y-%m-%d %H:%M', a.tradeTime / 1000 + 8 * 3600, 'unixepoch') as 日期, case when a.type = 1 then '收入' when a.type = 0 then '支出' end as 收支类型, case when a.type = 1 then (select case when (select b.currencyType from t_account b where b.accountPOID = a.sellerAccountPOID) = 'CNY' then a.buyerMoney else (select round(a.buyerMoney * d.rate, 2) from (select b.currencyType from t_account b where b.accountPOID = a.sellerAccountPOID) c, t_exchange d where c.currencyType = d.sell) end) when a.type = 0 then (select case when (select b.currencyType from t_account b where b.accountPOID = a.buyerAccountPOID) = 'CNY' then a.buyerMoney else (select round(a.buyerMoney * d.rate, 2) from (select b.currencyType from t_account b where b.accountPOID = a.buyerAccountPOID) c, t_exchange d where c.currencyType = d.sell) end) end as 金额, case when a.type = 1 then (select d.name from (select b.parentCategoryPOID from t_category b where b.categoryPOID = a.buyerCategoryPOID) c, t_category d where c.parentCategoryPOID = d.categoryPOID) when a.type = 0 then (select d.name from (select b.parentCategoryPOID from t_category b where b.categoryPOID = a.sellerCategoryPOID) c, t_category d where c.parentCategoryPOID = d.categoryPOID) end as 类别, case when a.type = 1 then (select b.name from t_category b where b.categoryPOID = a.buyerCategoryPOID) when a.type = 0 then (select b.name from t_category b where b.categoryPOID = a.sellerCategoryPOID) end as 子类, '日常账本' as 所属账本, case when a.type = 1 then (select b.name from t_account b where b.accountPOID = a.sellerAccountPOID) when a.type = 0 then (select b.name from t_account b where b.accountPOID = a.buyerAccountPOID) end as 收支账户, a.memo as 备注, (select c.name from t_transaction_projectcategory_map b, t_tag c where b.transactionPOID = a.transactionPOID and b.projectCategoryPOID = c.tagPOID and b.type = 2) as 标签, '' as 地址 FROM t_transaction a order by a.tradetime desc;''' # 运行一个查询语句 select_sql = sql # 使用游标执行查询 cursor.execute(select_sql) # 获取所有查询结果 results = cursor.fetchall() # 打印结果 for row in results: print(row) # 将结果转换为DataFrame columns = <div class="flex-column"></div> for column in cursor.description] # 获取列名 df = pd.DataFrame(results, columns=columns) # 关闭游标和连接 cursor.close() conn.close() # 使用xlwt库将DataFrame写入Excel文件 excel_filename = '一木记账导入.xls' # 创建一个Excel工作簿 wb = xlwt.Workbook() # 添加一个工作表 ws = wb.add_sheet('Sheet 1') # 将DataFrame数据写入Excel工作表 for col_num, col_data in enumerate(df.columns): ws.write(0, col_num, col_data) # 写入列名 for row_num, row_data in enumerate(df.values): for col_num, col_data in enumerate(row_data): ws.write(row_num + 1, col_num, col_data) # 写入单元格数据 # 保存Excel文件 wb.save(excel_filename) print(f'数据已成功写入到 {excel_filename}') ``` 完成后生成的文件效果如下: ![请输入图片描述](http://static.fox-9.com/uploads/2024/05/16/image-20240516212101797.png!webp) 接下来就很简单了,使用导出好的这个xls文件,在一木记账中打开或者点导入即可成功导入啦! 希望能对大家有所帮助,谢谢! 最后修改:2024 年 05 月 16 日 © 允许规范转载 赞 赠人玫瑰,手留余香