搞技術的大都比較純粹,比較實在,除了工資之外基本就沒有別的收入了(少部分人能接外包賺外塊)。或許是迫於生活的壓力,或許是不甘於固定的工資,或許是出於技術人骨子裡的好奇,亦或是這幾年關於理財投資的大力宣傳、門檻降低,理財越來越被我們所接受,並開始嘗試股票、基金、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()
複製程式碼
微信好友、群自動回覆
我想看資料的時候如何去看呢?去伺服器上執行下指令碼這方式太low了吧,藉助微信機器人,就像你跟朋友聊天一樣,發訊息“最新資料”,那他就立即回覆最新訊息給你,這個方式看起來不錯,實現完成後有幾個朋友覺得不錯,也想看資料,那我乾脆將這些需要看資料的朋友都拉倒一個群裡吧,回覆訊息群裡所有使用者都看得到很方便了,技術實現主要藉助了itchat
模組(itchat主要通過網頁版微信介面處理資料,網頁版微信很多使用者無法登陸了,也就沒有辦法使用itchat),程式碼如下
import itchat
from tdw_data_statistics_now import get_now_data
# 處理好友訊息
@itchat.msg_register(itchat.content.TEXT)
def text_reply(msg):
if msg['Text'].startswith('命令:'):
message = msg['Content'].split('命令:')[1]
msg.user.send('正在處理你的命令:%s' %(message))
if '最新資料' == msg['Text']:
message = get_now_data()
msg.user.send(message)
# 處理群聊訊息
@itchat.msg_register(itchat.content.TEXT, isGroupChat=True)
def text_reply(msg):
#itchat.send(u'@%s\u2005I received: %s' % (msg['ActualNickName'], msg['Content']), msg['FromUserName'])
if '最新資料' == msg['Text']:
message = get_now_data()
msg.user.send(message)
#message = getMessage()
#itchat.send(message, toUserName='filehelper')
itchat.auto_login(True, enableCmdQR=True)
itchat.run(True)
複製程式碼
微信公眾號自動回覆
設想一個應用場景,如果有很多人需要這個資料怎麼處理呢?讓他們都加一下我的微信或把他們都給加到一個群裡固然可以,只是不夠優雅,這裡想到了微信公眾號,當使用者關注公眾號後,回覆“最新資料”可把最新資料自動回覆給使用者是不是就優雅很多了。然後就寫了個機器人自動處理,主要藉助werobot
模組實現。
微信公眾號可以配置為開發者模式,也就是開發者可以提供一個http介面,公眾號會把收到的所有訊息傳送給開發者提供的介面,伺服器接收到資料後判斷資料型別,對資料做處理,這裡需要用到web服務,所以引入Django
- url
from django.urls import path
from werobot.contrib.django import make_view
from official.robot import robot
urlpatterns = [
path('robot/', make_view(robot)),
]
複製程式碼
- 機器人程式
# cat official/robot.py
import re
from werobot import WeRoBot
from werobot.replies import ImageReply
from official.backends.get_tdw_data import get_now_data
robot = WeRoBot(enable_session=False,
token='',
APP_ID='',
APP_SECRET='')
# 新使用者關注自動回覆
@robot.subscribe
def subscribe(message):
return '''來了?坐,好戲馬上開始
回覆[最新資料]獲取最新資料更新'''
# 聯絡二維碼,使用者輸入"聯絡"關鍵字回覆作者二維碼
@robot.filter(re.compile(".*?聯絡.*?"))
def contact(message):
return ImageReply(
message=message,
media_id="DBO8qVu-8bwNF9O7o8wCyRs4awfVTjA_WuPoLkj33B1C8ZX9JVdmw30zZo9l8ovx"
)
# 處理文字訊息
@robot.text
def hello(message):
try:
msg = message.content.strip().lower()
if re.compile("最新.*").match(msg):
tdw_zx_now_data = get_now_data()
if tdw_zx_now_data:
return tdw_zx_now_data
else:
return '暫時無法獲取資料,請稍後再試'
else:
return '請輸入[ 最新資料 ]獲取智享資料更新\n\n如有疑問可以回覆[ 聯絡作者 ]與我聯絡'
except Exception as e:
print(e)
複製程式碼
- 後臺處理程式
import json
import datetime
from django_redis import get_redis_connection
cache = get_redis_connection('default')
def get_now_data():
now_time = datetime.datetime.now()
prev_time = now_time - datetime.timedelta(minutes=1)
# 直接去快取取資料
redis_value = cache.get('tdw_zx_now_'+prev_time.strftime('%Y%m%d%H%M'))
if redis_value:
jsondata = json.loads(redis_value.decode())
daihuan = '今日待還金額:%s萬元' %jsondata.get('daihuan')
chengjiao = '當前已成交金額:%s萬元' %jsondata.get('chengjiao')
avg_hour_money = '完成還款預估每小時需要成交:%s萬元' %jsondata.get('avg_hour_money')
return '''%s\n%s\n%s\n%s''' %(prev_time.strftime('%Y-%m-%d %H:%M'), daihuan, chengjiao, avg_hour_money)
else:
return None
複製程式碼
總結
- 學會投資理財
- 別人賺錢的方式可能跟自己想象的不一樣
- 用技術解決生活中的實際問題,不僅能提高能力,還能獲得更大的滿足