SpringBoot + Thymeleaf + Bootstrap + 随手记 实现自动记账 一
前情提要
在用随手记勤勤恳恳记了6年账之后,突然有天开始变懒了。既然每笔消费都从手机上支出,为什么不写个程序来自动完成这个动作呢?
思路
目前日常支出无非两种,一种是支付宝,一种是微信。说到这个,再次吐槽微信支付做的真的是一坨shit。
因为都绑定了信用卡,所以只要是能使用信用卡的地方都优先使用了信用卡来付款,这样的好处,每笔消费记录都以电子账单的形式发送到邮箱。
基于以上,一个自动记账的程序大体思路就浮现了。
第一步 读取邮箱邮件内容,取得招商银行的的账单信息,分析账单得到每笔消费
第二步 获取到"随手记"系统的支出类别的类别编码
第三步 根据消费店铺的收款方和付款时间来识别这笔消费属于什么分类,比如 只要含有地铁或者深圳通 就可以识别为公共交通类的支出
第四步 把识别好的账单记录保存到随手记
实现
本篇博客没想要怎么介绍从头搭建一整套框架,只是想实现一个身边的需求来方便自己,重点是引发各位怎么样去实现自己身边的需求,所以不会介绍怎么从头搭建一套框架。
项目大体分以下7个部分
1.搭建后台框架
在此推荐一个SpringBoot + mybatis 的种子项目
Git地址: https://github.com/lihengming/spring-boot-api-project-seed
2.数据库表设计
- 首先我们需要一张邮件
email
表来记录收到邮件的日期,邮件ID等防止重复读取邮件 - 然后我们需要账单流水表
account_flow
来保存从邮件中解析到的具体消费信息 - 保存从随手记上同步到的类型我们需要一张类型
ssj_type
表 - 最后我们需要一张
relation
表,来保存随手记系统中的类型ID和我们的消费店铺关系
以上,总共4张表。
3.引入JavaMail 组件 解析邮件内容
java 使用 JavaMail 组件网络上已经有大量资料,推荐系列文章JavaMail学习笔记
此处有几个要注意的地方
- 邮箱必须要开启POP3接受 设置->账户 里面开启
- 以QQ邮箱为例默认抓取的是最近30天的邮件,可自行更改
4.解析并存储邮件里的账单内容
为了解析邮件内容,需要引入 jsoup包 Maven坐标如下
<dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.11.3</version> </dependency>
参考资料: jsoup中文学习手册
以招商银行账单为例 ,把邮件内容另存为html可以看的如下图。
我们只要解析这个部位的html,拿到表格内容也就得到了具体的消费信息
右键查看源码,可以看到,每行的内容都在一个id为 fixBand28 的span标记下
我们可以使用 jsoup 来解析这段html, 关键代码如下
Document doc = Jsoup.parse("邮件正文");
Elements spans = doc.select("[id=fixBand28]");
saveAccountFlow(spans, mailId);
/**
* 保存账务流水
* @param spans 招商银行账单表格TD
* @param mailId 内容来自的邮件ID
*/
private void saveAccountFlow(Elements spans, Integer mailId) {
List<AccountFlow> accountFlowList = new ArrayList<>();
for (Element span: spans) {
Elements cardNo = span.select("[id=fixBand29]");
Elements tradeDate = span.select("[id=fixBand30]");
Elements tradeTime = span.select("[id=fixBand31]");
Elements currency = span.select("[id=fixBand32]");
Elements store = span.select("[id=fixBand33]");
Elements money = span.select("[id=fixBand34]");
AccountFlow accountFlow = new AccountFlow();
accountFlow.setEmailId(mailId);
accountFlow.setCardNo(cardNo.text());
Date tradeDateTime = turnDateStr(tradeDate.text() +" " +tradeTime.text());
accountFlow.setTradeDate(tradeDateTime);
accountFlow.setCurrency(currency.text());
accountFlow.setStore(store.text());
accountFlow.setMoney(new BigDecimal(money.text()));
accountFlowList.add(accountFlow);
}
accountFlowService.save(accountFlowList);
}
至此,已经把账单内容从邮件中提取到了我们自己的数据库中,接下来是使用HttpClient来模拟登录随手记
5.爬取随手记获得所有类型
这部分思路是,打开随手记官网登录 用谷歌浏览器F12获得url,然后用httpclient来模拟登录。
这块遇到的第一个问题,随手记官网登录虽然没有使用验证码机制拦截,但是在传输时是用了JS加密密码。加密的方式是,在页面加载时获得一个vccode+uid 然后在登录时候 要在js里用vccode+密码+帐号 混淆密码后传输。
好消息是在 Java 8 中的Nashorn 引擎实现了用java去执行js脚本,省去了要用java实现一遍js加密逻辑的问题。
在随手记登录界面,右键查看源代码找到
<script type="