之前写了一篇保存到Excel版的,但毕竟随着数据量的增大,Excel不是长久之计,所以研究了一下SQLite数据库,SQLite有个好处,不用另外安装程序,Python自带,而且是单文件,方便拷贝转移。
和保存到Excel的主要区别是,可以先将爬取到的数据保存在Python的二维列表中,然后一次性写入SQLite数据库,Python的二维列表的数据容量非常巨大,就是从内存安全性来说,不是很严谨。
以下是代码:
# -*- coding: utf-8 -*-
"""
根据FundList.xls文件中的基金列表,从小熊同学网站API读取基金净值信息,写入到
SQLite数据库中
@author: RockyCoder
"""
'''
第一部分:引用
'''
import xlrd
import sqlite3
import requests
import json
'''
第二部分:函数
'''
# 将长度不定的列表,按照指定长度切割为子列表后,整合成嵌套列表。
def LineList(SrcList,LineLen):
# 新列表,供返回
NewList=[]
# 记录源列表长度
SrcLen=len(SrcList)
# 判断是否需要分行
if SrcLen<LineLen:
return SrcList
else:
# 若需要分行,计算行数和剩余个数
LineCnt=int(SrcLen/LineLen)
LeftLen=SrcLen%LineLen
# 在行数范围内,通过列表切片方式,获取指定长度的子列表
for i in range(LineCnt):
Lines=SrcList[(i*LineLen):(i*LineLen+LineLen):1]
# 拼接到返回列表中
NewList.append(Lines)
# 如果剩余个数大于0,则将剩余部分通过切片方式,以子列表形式拼接到返回列表
if LeftLen>0:
NewList.append(SrcList[SrcLen-LeftLen:])
return NewList
# 将指定的列表转换为以逗号分隔的字符串
def CSVString(SrcList):
TempString=''
for i in range(len(SrcList)):
if type(SrcList[i]) is int:
TempString=TempString+str(SrcList[i])+','
elif type(SrcList[i]) is str:
TempString=TempString+SrcList[i]+','
else:
print(type(SrcList[i]))
return TempString[:-1]
'''
第三部分:操作
'''
# 打开基金原始表格
rdBook=xlrd.open_workbook('FundList.xls',formatting_info=True)
rdSheet=rdBook.sheet_by_name('Sheet1')
# 准备数据库
conn=sqlite3.connect('Fund.db')
c=conn.cursor()
# 初始化基础变量
url="https://api.doctorxiong.club/v1/fund?code="
SQLList=[]
TempList=[]
# 读取完整基金列表
fundCnt=int(rdSheet.cell_value(0,2))
FundList=[]
for j in range(2,fundCnt+1):
FundCode=str(int(rdSheet.cell_value(j,1)))
if len(FundCode)>0:
FundList.append('0'*(6-len(FundCode))+FundCode)
else:
print("基金代码不得为空")
exit(0)
# 按照20个一组,形成子列表
Lines=[]
Lines=LineList(FundList,20)
for l in Lines:
# 根据逐个子列表,形成URL
FinalURL=url+CSVString(l)
strHTML=requests.get(FinalURL)
contents=json.loads(strHTML.text)
for k in contents["data"]:
# 将有用字段写成一行
TempList.extend((k['code'],k['name'],k['netWorth'],k['dayGrowth'],k['lastWeekGrowth'],k['lastMonthGrowth'],k['lastThreeMonthsGrowth'],k['lastSixMonthsGrowth'],k['lastYearGrowth'],k['netWorthDate']))
# 将每一行集合成一个二维表
SQLList.append(TempList)
TempList=[]
# 检查当前净值是否已写入过数据库
# 将净值列表写入数据库
c.executemany("INSERT INTO 产品库 (基金代码,基金名称,单位净值,日涨幅,周涨幅,月涨幅,季涨幅,半年涨幅,年涨幅,更新日期) VALUES (?,?,?,?,?,?,?,?,?,?)", SQLList)
conn.commit()
conn.close()
SQLite相关数据表的结构
为了避免重复导入,在数据库层面做了一个多字段联合的约束,这个约束必须在创建数据表的SQL语句中体现,代码如下:
CREATE TABLE 产品库 (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
基金代码 CHAR (6),
基金名称 CHAR,
单位净值 DECIMAL,
日涨幅 DECIMAL,
周涨幅 DECIMAL,
月涨幅 DECIMAL,
季涨幅 DECIMAL,
半年涨幅 DECIMAL,
年涨幅 DECIMAL,
更新日期 DATE,
UNIQUE (
基金代码,
更新日期
)
ON CONFLICT REPLACE
);