搞技术的大都比较纯粹,比较实在,除了工资之外基本就没有别的收入了(少部分人能接外包赚外块)。或许是迫于生活的压力,或许是不甘于固定的工资,或许是出于技术人骨子里的好奇,亦或是这几年关于理财投资的大力宣传、门槛降低,理财越来越被我们所接受,并开始尝试股票、基金、P2P、XX宝等各种理财产品,本文所讲与P2P有关,但不打广告,只讲技术,顺便说明:投资有风险,理财需谨慎,我们赚钱不容易,不能给打了水漂。
背景介绍某公司的理财产品有如下特点:
公司分别有12,18,24,36个月的固定期限理财产品,期限越长利率越高
投资用户可将债权申请转给其他投资人,转出时的利率你可以自行控制
你也可以通过平台借钱,借钱金额不能超过在投金额的3倍,所谓加杠杆
有一部分用户(行话叫牛)就靠平台活动或高息的时候借钱加杠杆投资,需要还钱的时候通过债权转让平台转让标还借款,通过买入和卖出时的利率差获得额外收益。这中间比较关键的一点就是转出时的利率,利率低收益就高(但太低就没有人接手了,转不出去还不了借款就要支付高额罚金),利率又跟当天待还的金额和已成交的金额有直接关系,那么如果能及时获取这两个数据就大概知道自己标多少利率能转手成功了。
我们接下来的技术实现就主要跟获取这两个数据,以及如何及时的展示数据有关。
服务和工具python3.4
mysql5.7
redis2.8
django2.0
技术实现只是为了技术研究,没有商用,代码和架构以实现需求为目的,未做优化,且非专业开发,凑合看
抓取数据翻了一遍平台官网发现有个页面直接展示了转让标的详细信息,无需登录,且是通过ajax方式异步加载的json字符串(但是json字符串里套了一堆的html代码,不知道咋设计的)的方式渲染页面的,那抓取工作简单多了,写了个抓取脚本,流程为:访问页面接口 --> 取到数据 --> 简单处理 --> 录入数据库,抓取脚本直接放在计划任务里每三分钟执行一次,脚本内容如下:
import re import time import datetime import requests import pymysql # 连接mysql数据库 db = pymysql.connect("127.0.0.1","root","passwd","pzp") cursor = db.cursor() for i in range(1, 9999): data = { "RepaymentTypeId": 0, "pagesize": 1, "pageindex": i, "type": 1, "status": 2, "startDeadLine": 0, "endDeadLine": 0, "rate": 0, "beginRate": 0, "endRate": 0, "strkey": None, "orderby": 15, "unitStart": 0, "unitEnd": 0 } try: r = requests.post('https://www.tuandai.com/pages/zSharePlan/getZXList', data=data).json() if r.get('code') == 0: html = r.get('data').get('projectListHtml') dr = re.compile(r'<[^>]+>', re.S) dd = dr.sub('', html).split() # 截取单号,只取字符串中的数字 order_num = ''.join(re.compile('\d+').findall(dd[0])) # 查询mysql数据库 cursor.execute("select order_num from tdw_zx_done where order_num = %s" %order_num) # 获取到查询结果 ex = cursor.fetchone() # 判断单号是否已经记录过 if ex is None: # 如果单号没有记录过,则计数器置为0 x = 0 publish_time = datetime.datetime.strptime( '20%s-%s-%s %s:%s:%s' %(order_num[0:2],order_num[2:4],order_num[4:6],order_num[6:8],order_num[8:10],order_num[10:12]), '%Y-%m-%d %H:%M:%S' ) # ‘元’单位都替换成‘万元’,并去掉汉字 a = dd[1].split(':')[1] if '元' in a: money = int(''.join(re.compile('\d+').findall(a))) / 10000 else: money = a.replace('万','') # 取利率 rate = dd[4].replace('%','') # 计算还款日期,借款日期 + 借款时间 days = ''.join(re.compile('\d+').findall(dd[6])) repay = (publish_time + datetime.timedelta(days=int(days))).strftime('%Y-%m-%d') print(publish_time, order_num, money, rate, days, repay) # 往数据库里插入数据并提交 sql = "INSERT INTO tdw_zx_done VALUES('%s', %s, %s, %s, %s, '%s')" %(publish_time, order_num, money, rate, days, repay) cursor.execute(sql) db.commit() else: # 如果单号已经记录过,则计数器加1 x += 1 # 如果单号已录入过数据库,则返回 print('单号已录入:%s' %str(order_num)) # 判断如果有连续200个单号都已经录入过数据库,则跳出循环 if x == 200: break time.sleep(0.02) else: print(r) except Exception as e: print(e) db.close() 数据处理及缓存上边已经获取到了原始数据,接下来需要对原始数据进行清洗,取自己需要的今日待还及实时成交,并写入缓存,写入缓存的目的是公众号并发查询的情况下,直接去缓存取数据,减小对数据库的压力,这个脚本程序也放在计划任务里每分钟执行
import os import sys import json import datetime from decimal import Decimal os.chdir(sys.path[0]) from connection import rediscon, mysqlcon mc, rc = mysqlcon().cursor(), rediscon() def cache_now_data(): today = datetime.date.today().strftime('%Y-%m-%d') tj, th, = '', '' try: # 成交数据统计 mc.execute("select count(1),sum(money) from tdw_zx_done where DATE(publish_time)='%s';" %today) tj = mc.fetchone() tjie = '借款人数:%s 借款金额:%s' %(tj[0], tj[1]) # 待还数据统计 mc.execute("select count(1),sum(money) from tdw_zx_done where repay='%s';" %today) th = mc.fetchone() thuan = '待还人数:%s 待还金额:%s' %(th[0], th[1]) # 完成还款预估时间 if th[0]: tomorror_date = datetime.date.today() + datetime.timedelta(days=1) tomorror_time_str = tomorror_date.strftime('%Y-%m-%d 00:00:00') tomorror_time_format = datetime.datetime.strptime(tomorror_time_str, '%Y-%m-%d %H:%M:%S') last_hour = (tomorror_time_format - datetime.datetime.now()).seconds / 60 / 60 avg_hour_money = round(Decimal(th[1] - tj[1]) / Decimal(last_hour), 4) avg_hour_money = avg_hour_money if avg_hour_money > 0 else 0 else: avg_hour_money = '无今日待还数据,无法计算' # 按小时统计详情 mc.execute("select Hour(publish_time) as Hour,count(1),sum(money) from tdw_zx_done where DATE(publish_time) ='%s' group by Hour;" %today) tdetail = mc.fetchall() dl = '小时 | 成交额(万元)\n' for i in tdetail: dl += str(i[0]) + ' | ' + str(i[2]) + '\n' except Exception as e: print('数据库操作异常:%s' %e) try: key = datetime.datetime.now().strftime('%Y%m%d%H%M') val = {"daihuan":str(th[1]),"chengjiao":str(tj[1]),"avg_hour_money":str(avg_hour_money)} print(rc.set('tdw_zx_now_'+key, json.dumps(val), ex=7200)) except Exception as e: print('缓存操作异常:%s' %e) if __name__ == '__main__': cache_now_data() 微信好友、群自动回复