内容简介:支付宝出了年度账单,再也不敢说今年没花多少钱了,想了想平时大额支付都用支付宝,小额支付用的微信,这微信的账单数据还没统计进来,嗯...微信出了《2018微信数据报告》但没有消费年度账单,微信钱包的账单中有个月账单功能能看到个大概每个月的支出收入,也可以导出账单记录只能导出三个月,统计一年的还得分四次导出是csv格式的还得合并什么的,数据比较简单感觉没啥用,我的想法是先看微信账单页面进入的时候加载了个进度条明显是H5的页面
支付宝出了年度账单,再也不敢说今年没花多少钱了,想了想平时大额支付都用支付宝,小额支付用的微信,这微信的账单数据还没统计进来,嗯...
微信出了《2018微信数据报告》但没有消费年度账单,微信钱包的账单中有个月账单功能能看到个大概每个月的支出收入,也可以导出账单记录只能导出三个月,统计一年的还得分四次导出是csv格式的还得合并什么的,数据比较简单感觉没啥用,我的想法是 通过接口请求的方式获取2018年所有微信消费账单,导入数据库后通过 sql 查询统计信息
1、调试微信获取微信账单接口
先看微信账单页面进入的时候加载了个进度条明显是H5的页面
1.1、先打开微信调试功能,在微信里打开这个链接(地址:debugx5.qq.com),勾选调试 1.2、将Android手机连上电脑,在手机开发者选项里开启USB调试,微信打开账单页面。
1.3、在chrome上打开 Inspect页面 ,点击账单inspect。
1.4、滑下账单列表拉取下数据就能看到请求了,还有response中返回的json信息
2、分析微信账单列表接口
可以看到账单Api是get请求接口为: wx.tenpay.com/userroll/us…
请求参数为和返回结果json结构如下,record就是具体的账单列表
通过查看请求参数和返回结果发现,exportkey不变,只需要把请求返回的结果中的参数拼接到下一次请求的query参数里面可以一直请求翻页,query中last_create_time和start_time是一样的,是翻页时的上一笔账单的时间,可以定为小于2018年1月1日的时间就跳出循环,这样就获取到2018年的所有账单。
当然这里面有坑,模拟请求的时候最好是所有的header都带上,这里面的参数是有过期时间的并且很快就过期了,分别是exportkey、request header中的Cookie里的userroll_encryption、userroll_pass_ticket,其它的直接copy即可。这里没有找到它们的生成规律,过期了需要重新通过抓取接口查看这三个参数。
3、通过编写代码获取账单保存至数据库
最近在看后端开发,一直用 Java 写Android还没写过服务端代码,正好来练练手。IntelliJ IDEA和Android Studio几乎一样上手比较友好。
先通过账单的json建好数据库表,再建立模型bean,再写业务逻辑,做后数据库操作。
主要逻辑代码如下:
// @Async("taskExecutor") public void createGetBillTask( String exportkey, String userroll_encryption, String userroll_pass_ticket) { DemoApplication.logger.warn("创建任务:" + exportkey); //copy head头信息 Map<String, String> headMaps = new LinkedHashMap<>(); headMaps.put("Accept", "*/*"); headMaps.put("Accept-Encoding", "gzip, deflate"); headMaps.put("Accept-Language", "zh-CN,en-US;q=0.8"); headMaps.put("Connection", "keep-alive"); headMaps.put("Cookie", "userroll_encryption=" + userroll_encryption + "; userroll_pass_ticket=" + userroll_pass_ticket); headMaps.put("Host", "wx.tenpay.com"); headMaps.put("Q-Auth", "需要copy"); headMaps.put("Q-GUID", "需要copy"); headMaps.put("Q-UA2", "需要copy"); headMaps.put("Referer", "https://wx.tenpay.com/?classify_type=0"); headMaps.put("User-Agent", "Mozilla/5.0 (Linux; Android 8.0; MIX 2 Build/OPR1.170623.027; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044408 Mobile Safari/537.36 MMWEBID/4508 MicroMessenger/7.0.1380(0x27000038) Process/tools NetType/WIFI Language/zh_CN"); headMaps.put("X-DevTools-Emulate-Network-Conditions-Client-Id", "需要copy"); headMaps.put("X-Requested-With", "com.tencent.mm"); HttpHeaders headers = new HttpHeaders(); headers.clear(); headers.setAll(headMaps); headers.setExpires(0); headers.setCacheControl("private, no-store, max-age=0"); HttpEntity entity = new HttpEntity(headers); OrderResp lastResp = null; while (true) { String url = "https://wx.tenpay.com/userroll/userrolllist?classify_type=0&count=" + PAGE_SIZE + "&sort_type=1"; Map<String, Object> queryMaps = new LinkedHashMap<>(); if (lastResp != null) { //小于2017-12-31 跳出 if (lastResp.getLast_create_time() < 1514736000) { break; } url += "&exportkey={exportkey}&last_bill_id={last_bill_id}&last_bill_type={last_bill_type}&last_create_time={last_create_time}&last_trans_id={last_trans_id}&start_time={start_time}"; queryMaps.put("exportkey", exportkey); queryMaps.put("last_bill_id", lastResp.getLast_bill_id()); queryMaps.put("last_bill_type", lastResp.getLast_bill_type()); queryMaps.put("last_create_time", lastResp.getLast_create_time()); queryMaps.put("last_trans_id", lastResp.getLast_trans_id()); queryMaps.put("start_time", lastResp.getLast_create_time()); } try { URI uri = restTemplate.getUriTemplateHandler().expand(url, queryMaps); //网络请求账单接口拉取数据 ResponseEntity<OrderResp> resp = restTemplate.exchange(uri, HttpMethod.GET, entity, OrderResp.class); DemoApplication.logger.warn("任务信息:" + uri.toString() + "\nheader:" + resp.getHeaders().toString()); if (!resp.getStatusCode().is2xxSuccessful()) { DemoApplication.logger.warn("任务请求网络失败:" + resp.toString()); break; } OrderResp body = resp.getBody(); if (body == null || body.getRet_code() != 0 || body.getRecord() == null || body.getRecord().isEmpty()) { DemoApplication.logger.warn("任务请求失败:" + url); break; } //写入数据库 orderDao.saveAll(records); orderDao.flush(); lastResp = body; long timestamp = lastResp.getLast_create_time(); String format = sdf.format(new Date(timestamp * 1000)); DemoApplication.logger.warn("任务进行中:" + exportkey + ":已导入至:" + format); } catch (Exception e) { e.printStackTrace(); } try { //间隔1s Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } DemoApplication.logger.warn("完成任务" + exportkey ); } 复制代码
4、通过SQL输出年度账单统计信息
完成入库后我大概有七百多条账单记录,下面举例分析一波
4.1、按年度总支出与收入
select sum(case when fee_attr='positive' then fee*0.01 else fee*-0.01 end ) as money, sum(case when fee_attr='positive' then fee*0.01 else 0 end ) as 总收入, sum(case when fee_attr='negtive' then fee*-0.01 else 0 end ) as 总支出 from orders where timestamp < 1546272000 and timestamp> 1514736000 复制代码
4.2、按消费类型统计
select classify_type,count(*),sum(fee * 0.01) as feesum , (select type_str FROM order_type WHERE orders.classify_type=order_type.type) as typestr from orders where timestamp < 1546272000 and timestamp> 1514736000 group by classify_type order by feesum desc; 复制代码
4.3、按月份收入支出净额统计
select FROM_UNIXTIME(timestamp,'%Y-%m') as time, sum(case when fee_attr='positive' then fee*0.01 else 0 end ) as feesumpos, sum(case when fee_attr='negtive' then fee*-0.01 else 0 end ) as feesumneg, sum(case when fee_attr='positive' then fee*0.01 else fee*-0.01 end ) as feesumdda from orders where timestamp < 1546272000 and timestamp> 1514736000 group by time order by time desc; 复制代码
4.4、按消费数量统计
select title,count(title) as 数量 from orders where timestamp < 1546272000 and timestamp> 1514736000 group by title order by 数量 desc; 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 年度账单/总结类产品该注意些什么?
- BUF早餐铺 | Intel芯片漏洞风波升级;支付宝深夜就年度账单道歉;PiKarma Python脚本可用于识别恶...
- 微信小程序数据统计和错误统计的实现
- 机器学习数学基础:数理统计与描述性统计
- Java8中使用stream进行分组统计和普通实现的分组统计的性能对比
- RecyclerView 的曝光统计
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入浅出程序设计(中文版)
Paul Barry、David Griffiths / 蒋雁翔、童健 / 东南大学出版社 / 2012-1 / 98.00元
《深入浅出程序设计(中文版)》介绍了编写计算机程序的核心概念:变量、判断、循环、函数与对象——无论运用哪种编程语言,都能在动态且多用途的python语言中使用具体示例和练习来运用并巩固这些概念。学习基本的工具来开始编写你感兴趣的程序,而不是其他人认为你应该使用的通用软件,并对软件能做什么(不能做什么)有一个更好的了解。当你完成这些,你就拥有了必要的基础去使用任何一种你需要或想要学习的语言或软件项目......一起来看看 《深入浅出程序设计(中文版)》 这本书的介绍吧!