1. 项目背景与核心价值
金融数据领域长期存在信息孤岛问题,基金产品、基金经理、投资标的之间的关联关系往往隐藏在非结构化数据中。传统基于表格的数据存储方式难以直观展现这些复杂网络关系,这正是知识图谱技术能够大显身手的领域。
"天天基金网"作为国内领先的基金数据平台,包含了超过8000只公募基金的详细档案、净值走势、持仓明细等关键数据。通过系统化采集这些数据并构建知识图谱,可以实现:
- 基金产品的多维关联查询(同类基金对比、持仓重合度分析)
- 基金经理能力圈可视化(历史管理产品业绩关联)
- 智能投顾基础数据支撑(基于图谱的关系推理)
这个项目完整演示了从原始数据获取到知识图谱落地的全流程,重点解决金融领域特有的数据采集合规性、非结构化数据处理、动态关系建模三个技术难点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据采集方案设计
2.1 目标数据识别
通过分析天天基金网的页面结构,确定需要采集的核心实体类型:
- 基金产品基础信息(代码、名称、类型、成立日期)
- 基金经理信息(从业年限、管理产品历史)
- 基金持仓数据(股票/债券持仓明细及变动)
- 基金公司信息(管理规模、产品线)
- 业绩评价指标(夏普比率、最大回撤)
重要提示:采集频率需控制在网站反爬策略允许范围内,建议设置2-3秒的请求间隔,且避免在交易日开盘/收盘时段集中采集
2.2 采集技术实现
采用Python技术栈构建采集系统:
python复制import requests
from bs4 import BeautifulSoup
import pandas as pd
def fetch_fund_basic(fund_code):
url = f"https://fund.eastmoney.com/{fund_code}.html"
headers = {'User-Agent': 'Mozilla/5.0'}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, 'html.parser')
# 提取基础信息示例
fund_name = soup.select('.fundDetail-tit')[0].get_text(strip=True)
info_items = soup.select('.infoOfFund td')
fund_type = info_items[0].get_text(strip=True).split(':')[-1]
return {
'code': fund_code,
'name': fund_name,
'type': fund_type
}
关键参数说明:
- User-Agent模拟浏览器访问
- CSS选择器定位关键数据节点
- 异常处理机制(网络重试、数据校验)
3. 数据清洗与结构化处理
3.1 金融数据清洗要点
基金数据清洗需要特别注意:
- 百分比数据统一转换(如"5.23%"→0.0523)
- 日期格式标准化(不同页面可能使用"2023-01-01"或"2023年1月1日")
- 缺失值特殊处理(持仓数据可能存在季度缺失)
- 文本数值分离(如"招商银行(600036)"需拆解为名称和代码)
清洗代码示例:
python复制def clean_percentage(value):
if isinstance(value, str) and '%' in value:
return float(value.strip('%')) / 100
return value
def split_stock_info(text):
import re
pattern = r'^(.*?)\((\d{6})\)$'
match = re.match(pattern, text)
if match:
return match.group(1), match.group(2)
return text, None
3.2 关系数据建模
设计五类核心关系:
- 基金-基金经理:管理关系
- 基金-持仓股票:投资关系
- 基金-基金公司:归属关系
- 股票-行业:分类关系
- 基金经理-从业经历:时间轴关系
使用如下结构存储清洗后的数据:
csv复制# 关系数据示例
source_id,source_type,relation,target_id,target_type,weight,date
110022,fund,managed_by,305,manager,1.0,20230101
600036,stock,belongs_to,3,industry,1.0,
4. Neo4j图谱构建实战
4.1 环境配置建议
推荐使用Neo4j 4.4+版本,硬件配置要求:
- 测试环境:4核CPU/8GB内存/100GB SSD(支持千万级节点)
- 生产环境:建议集群部署,至少16GB内存
Docker快速启动命令:
bash复制docker run \
--name neo4j-fund \
-p7474:7474 -p7687:7687 \
-v $PWD/data:/data \
-v $PWD/import:/var/lib/neo4j/import \
-e NEO4J_AUTH=neo4j/password \
-e NEO4JLABS_PLUGINS='["apoc"]' \
neo4j:4.4
4.2 数据导入方案
批量导入建议采用以下两种方式:
- CSV直接导入(适合初始构建)
cypher复制LOAD CSV WITH HEADERS FROM 'file:///funds.csv' AS row
CREATE (f:Fund {
code: row.code,
name: row.name,
type: row.type,
establishDate: date(row.establishDate)
})
- APOC批量导入(适合增量更新)
cypher复制CALL apoc.load.json('file:///fund_relations.json') YIELD value
MATCH (src {id: value.source_id})
MATCH (dst {id: value.target_id})
CALL apoc.create.relationship(
src,
value.relation,
{weight: toFloat(value.weight)},
dst
) YIELD rel
RETURN count(rel)
4.3 图谱查询优化
针对金融查询场景的优化策略:
- 建立复合索引:
cypher复制CREATE INDEX fund_code_index FOR (f:Fund) ON (f.code);
CREATE INDEX stock_code_index FOR (s:Stock) ON (s.code);
- 常用查询模式示例:
cypher复制// 查询某基金经理管理的所有股票型基金
MATCH (m:Manager)-[:MANAGED_BY]->(f:Fund {type: '股票型'})
WHERE m.name = '张坤'
RETURN f.name, f.code
// 查找重仓某股票的所有基金(按持仓比例排序)
MATCH (s:Stock {code:'600036'})<-[r:HOLD]-(f:Fund)
RETURN f.name, r.weight
ORDER BY r.weight DESC
LIMIT 10
5. 典型问题解决方案
5.1 反爬虫应对策略
实测有效的技术方案:
- IP轮询:使用收费代理服务(注意合规性)
- 请求指纹模拟:定期更换User-Agent、Cookie
- 行为模式随机化:随机间隔(1-5秒)+鼠标移动轨迹模拟
- 验证码处理:第三方打码平台接入
法律提示:严格遵守《数据安全法》相关规定,采集数据不得包含个人隐私信息,且不得用于商业牟利
5.2 Neo4j性能调优
金融关系图谱特有的优化点:
- 适当使用虚拟节点(如季度时间节点)
- 对高频查询路径建立全路径索引
- 批量操作时关闭自动索引:
cypher复制CALL db.index.fulltext.awaitEventuallyConsistent()
:auto USING PERIODIC COMMIT 1000
LOAD CSV WITH HEADERS FROM 'file:///big.csv' AS row
CREATE (:Node {id: row.id})
5.3 数据更新机制
建议采用增量更新方案:
- 每日更新净值数据
- 季度更新持仓数据(财报公布后)
- 使用APOC的图比较算法识别变更:
cypher复制CALL apoc.periodic.iterate(
'MATCH (f:Fund) RETURN f',
'CALL apoc.load.jsonParams("https://api.example.com/fund/"+f.code, {}, null)
YIELD value SET f.latestNav = value.nav',
{batchSize:100, parallel:true}
)
6. 应用场景扩展
构建完成的基金知识图谱可支持:
-
智能投研系统:
- 产业链关联分析(通过持仓股票关联不同行业基金)
- 基金经理能力圈可视化
-
风险控制场景:
cypher复制// 检测基金投资集中度风险 MATCH (f:Fund)-[h:HOLD]->(s:Stock) WITH f, sum(h.weight) as totalWeight WHERE totalWeight > 0.7 RETURN f.name, totalWeight -
投资者教育工具:
- 基金关联网络可视化
- 历史业绩归因分析
实际部署建议:
- 前端使用D3.js或Echarts实现可视化
- 后端采用Spring Boot + Neo4j OGMS框架
- 定期生成图谱快照用于历史分析
