基金估值工具
1 from flask import Flask, render_template, request, jsonify 2 import requests 3 import json 4 import re 5 import time 6 from requests.adapters import HTTPAdapter 7 from urllib3.util.retry import Retry 8 9 app = Flask(__name__) 10 11 # 配置日志 12 import logging 13 logging.basicConfig(level=logging.INFO) 14 logger = logging.getLogger(__name__) 15 16 # 创建带重试机制的请求会话(增强请求头) 17 def create_session(): 18 """创建稳定的请求会话,包含完整的浏览器请求头""" 19 session = requests.Session() 20 retry = Retry( 21 total=5, 22 backoff_factor=2, 23 status_forcelist=[500, 502, 503, 504], 24 allowed_methods=["GET"] 25 ) 26 session.mount("http://", HTTPAdapter(max_retries=retry)) 27 session.mount("https://", HTTPAdapter(max_retries=retry)) 28 # 更完整的请求头,模拟真实浏览器 29 session.headers.update({ 30 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", 31 "Referer": "https://fund.eastmoney.com/", 32 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 33 "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", 34 "Accept-Encoding": "gzip, deflate, br", 35 "Connection": "keep-alive", 36 "Upgrade-Insecure-Requests": "1", 37 "Cache-Control": "max-age=0" 38 }) 39 return session 40 41 42 class FundValuation: 43 def __init__(self): 44 self.session = create_session() 45 46 def _extract_numeric_value(self, value): 47 """提取数值,处理百分比和空值""" 48 if not value or value == '': 49 return '0' 50 51 # 移除百分号并转换为数值 52 str_value = str(value).replace('%', '').strip() 53 try: 54 # 保留4位小数 55 return f"{float(str_value):.4f}" 56 except: 57 return '0' 58 59 def _get_1234567_fund(self, fund_code): 60 """1234567基金网数据源(优先)""" 61 try: 62 url = f"http://fundgz.1234567.com.cn/js/{fund_code}.js" 63 response = self.session.get(url, timeout=10) 64 response.raise_for_status() # 检查HTTP状态码 65 # 清理返回数据(处理可能的多余字符) 66 raw_data = response.text.strip() 67 # 提取JSON部分(兼容不同格式) 68 json_match = re.search(r'jsonpgz\((.*)\);', raw_data) 69 if not json_match: 70 raise ValueError("未找到JSON数据") 71 72 fund_info = json.loads(json_match.group(1)) 73 74 # 基础数据 + 扩展字段(默认值) 75 result = { 76 "code": fund_info["fundcode"], 77 "name": fund_info["name"], 78 "current_value": fund_info["gsz"], # 实时估值 79 "current_rate": f"{fund_info['gszzl']}%", # 实时涨幅 80 "nav": fund_info.get("dwjz", "暂无数据"), # 单位净值 81 "accum_nav": fund_info.get("ljjz", "暂无数据"), # 累计净值 82 "day_rate": "暂无数据", # 日涨跌幅 83 "time": fund_info["gztime"], 84 "source": "1234567基金网", 85 "status": "success", 86 # 新增收益率数据 87 "yield_1day": "暂无数据", 88 "yield_1week": "暂无数据", 89 "yield_1month": "暂无数据", 90 "yield_3month": "暂无数据", 91 "yield_6month": "暂无数据", 92 "yield_1year": "暂无数据", 93 "yield_2year": "暂无数据", 94 "yield_3year": "暂无数据", 95 "yield_since_inception": "暂无数据", # 成立以来收益率 96 "fund_type": "暂无数据", # 基金类型 97 "fund_company": "暂无数据" # 基金公司 98 } 99 return result 100 except Exception as e: 101 logger.warning(f"1234567基金网接口失败: {str(e)}") 102 return {"status": "failed", "error": str(e)} 103 104 def _get_eastmoney_fund(self, fund_code): 105 """东方财富网数据源(备用1)- 增强版,获取更多数据""" 106 try: 107 # 1. 获取基础估值数据 108 url = f"https://fund.eastmoney.com/pingzhongdata/{fund_code}.js" 109 response = self.session.get(url, timeout=10) 110 response.raise_for_status() 111 response.encoding = "utf-8" 112 113 raw_data = response.text.strip() 114 115 # 提取基础数据 116 fund_data = {} 117 # 匹配估值相关数据 118 gsz_match = re.search(r'gsz\s*:\s*"([^"]+)"', raw_data) 119 gszzl_match = re.search(r'gszzl\s*:\s*"([^"]+)"', raw_data) 120 gztime_match = re.search(r'gztime\s*:\s*"([^"]+)"', raw_data) 121 name_match = re.search(r'fS_name\s*:\s*"([^"]+)"', raw_data) 122 123 # 2. 获取详细数据(单位净值、收益率等) 124 detail_url = f"https://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code={fund_code}&page=1&per=1" 125 detail_response = self.session.get(detail_url, timeout=10) 126 detail_response.raise_for_status() 127 detail_response.encoding = "utf-8" 128 129 # 提取单位净值和累计净值 130 nav_match = re.search(r'<td class="tor bold">(.*?)</td>', detail_response.text) 131 accum_nav_match = re.search(r'<td class="tor bold">(.*?)</td>.*?<td class="tor bold">(.*?)</td>', detail_response.text, re.DOTALL) 132 133 # 3. 获取收益率数据 134 yield_url = f"https://fund.eastmoney.com/{fund_code}.html" 135 yield_response = self.session.get(yield_url, timeout=10) 136 yield_response.raise_for_status() 137 yield_response.encoding = "utf-8" 138 139 # 提取各类收益率 140 yield_patterns = { 141 "yield_1day": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?日涨跌幅', 142 "yield_1week": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近1周', 143 "yield_1month": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近1月', 144 "yield_3month": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近3月', 145 "yield_6month": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近6月', 146 "yield_1year": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近1年', 147 "yield_2year": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近2年', 148 "yield_3year": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近3年', 149 "yield_since_inception": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?成立以来', 150 "fund_type": r'<div class="fundInfoItem"><label>基金类型:</label><span>(.*?)</span></div>', 151 "fund_company": r'<div class="fundInfoItem"><label>基金公司:</label><span>.*?<a.*?>(.*?)</a>.*?</span></div>' 152 } 153 154 yield_data = {} 155 for key, pattern in yield_patterns.items(): 156 match = re.search(pattern, yield_response.text, re.DOTALL) 157 yield_data[key] = match.group(1).strip() if match else "暂无数据" 158 159 # 构建完整结果 160 result = { 161 "code": fund_code, 162 "name": name_match.group(1).strip() if name_match else "未知名称", 163 "current_value": gsz_match.group(1) if gsz_match else "暂无数据", 164 "current_rate": f"{gszzl_match.group(1)}%" if gszzl_match else "0%", 165 "nav": nav_match.group(1).strip() if nav_match else "暂无数据", 166 "accum_nav": accum_nav_match.group(2).strip() if accum_nav_match else "暂无数据", 167 "day_rate": yield_data["yield_1day"], 168 "time": gztime_match.group(1) if gztime_match else "暂无数据", 169 "source": "东方财富网", 170 "status": "success", 171 # 新增的收益率数据 172 "yield_1day": yield_data["yield_1day"], 173 "yield_1week": yield_data["yield_1week"], 174 "yield_1month": yield_data["yield_1month"], 175 "yield_3month": yield_data["yield_3month"], 176 "yield_6month": yield_data["yield_6month"], 177 "yield_1year": yield_data["yield_1year"], 178 "yield_2year": yield_data["yield_2year"], 179 "yield_3year": yield_data["yield_3year"], 180 "yield_since_inception": yield_data["yield_since_inception"], 181 "fund_type": yield_data["fund_type"], 182 "fund_company": yield_data["fund_company"] 183 } 184 185 return result 186 except Exception as e: 187 logger.warning(f"东方财富网接口失败: {str(e)}") 188 return {"status": "failed", "error": str(e)} 189 190 def _get_1234567fund_fund(self, fund_code): 191 """天天基金网数据源(备用2)- 增强版""" 192 try: 193 url = f"https://fund.eastmoney.com/{fund_code}.html" 194 response = self.session.get(url, timeout=10) 195 response.raise_for_status() 196 response.encoding = "utf-8" 197 198 # 提取基础数据 199 name_match = re.search(r'<div class="fundDetail-tit">.*?>(.*?)</div>', response.text, re.DOTALL) 200 value_match = re.search(r'<span id="gz_gsz">(.*?)</span>', response.text) 201 rate_match = re.search(r'<span id="gz_gszzl">(.*?)</span>', response.text) 202 time_match = re.search(r'<span id="gz_gztime">(.*?)</span>', response.text) 203 204 # 提取单位净值和累计净值 205 nav_match = re.search(r'<dt>单位净值\(.*?\)</dt>.*?<dd class="dataNums">(.*?)</dd>', response.text, re.DOTALL) 206 accum_nav_match = re.search(r'<dt>累计净值</dt>.*?<dd class="dataNums">(.*?)</dd>', response.text, re.DOTALL) 207 208 # 提取收益率数据 209 yield_patterns = { 210 "yield_1day": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?日涨跌幅', 211 "yield_1week": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近1周', 212 "yield_1month": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近1月', 213 "yield_3month": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近3月', 214 "yield_6month": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近6月', 215 "yield_1year": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近1年', 216 "yield_2year": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近2年', 217 "yield_3year": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?近3年', 218 "yield_since_inception": r'<span class="ui-font-middle ui-color-red">(.*?)</span>.*?成立以来', 219 "fund_type": r'<div class="fundInfoItem"><label>基金类型:</label><span>(.*?)</span></div>', 220 "fund_company": r'<div class="fundInfoItem"><label>基金公司:</label><span>.*?<a.*?>(.*?)</a>.*?</span></div>' 221 } 222 223 yield_data = {} 224 for key, pattern in yield_patterns.items(): 225 match = re.search(pattern, response.text, re.DOTALL) 226 yield_data[key] = match.group(1).strip() if match else "暂无数据" 227 228 if not all([name_match, value_match, rate_match, time_match]): 229 raise ValueError("部分基础数据提取失败") 230 231 return { 232 "code": fund_code, 233 "name": name_match.group(1).strip(), 234 "current_value": value_match.group(1).strip(), 235 "current_rate": f"{rate_match.group(1).strip()}%", 236 "nav": nav_match.group(1).strip() if nav_match else "暂无数据", 237 "accum_nav": accum_nav_match.group(1).strip() if accum_nav_match else "暂无数据", 238 "day_rate": yield_data["yield_1day"], 239 "time": time_match.group(1).strip(), 240 "source": "天天基金网", 241 "status": "success", 242 # 新增数据 243 "yield_1day": yield_data["yield_1day"], 244 "yield_1week": yield_data["yield_1week"], 245 "yield_1month": yield_data["yield_1month"], 246 "yield_3month": yield_data["yield_3month"], 247 "yield_6month": yield_data["yield_6month"], 248 "yield_1year": yield_data["yield_1year"], 249 "yield_2year": yield_data["yield_2year"], 250 "yield_3year": yield_data["yield_3year"], 251 "yield_since_inception": yield_data["yield_since_inception"], 252 "fund_type": yield_data["fund_type"], 253 "fund_company": yield_data["fund_company"] 254 } 255 except Exception as e: 256 logger.warning(f"天天基金网接口失败: {str(e)}") 257 return {"status": "failed", "error": str(e)} 258 259 def get_fund_value(self, fund_code): 260 """获取单只基金估值(三级备用数据源)""" 261 # 校验基金代码格式 262 if not fund_code.isdigit() or len(fund_code) not in [5, 6]: 263 return { 264 "code": fund_code, 265 "name": "无效代码", 266 "current_value": "暂无数据", 267 "current_rate": "暂无数据", 268 "nav": "暂无数据", 269 "accum_nav": "暂无数据", 270 "day_rate": "暂无数据", 271 "time": "暂无数据", 272 "source": "无", 273 "status": "failed", 274 "error": "基金代码必须为5-6位数字", 275 # 新增字段默认值 276 "yield_1day": "暂无数据", 277 "yield_1week": "暂无数据", 278 "yield_1month": "暂无数据", 279 "yield_3month": "暂无数据", 280 "yield_6month": "暂无数据", 281 "yield_1year": "暂无数据", 282 "yield_2year": "暂无数据", 283 "yield_3year": "暂无数据", 284 "yield_since_inception": "暂无数据", 285 "fund_type": "暂无数据", 286 "fund_company": "暂无数据" 287 } 288 289 # 第1优先级:1234567基金网 290 result = self._get_1234567_fund(fund_code) 291 if result["status"] == "success": 292 return result 293 294 # 第2优先级:东方财富网 295 result = self._get_eastmoney_fund(fund_code) 296 if result["status"] == "success": 297 return result 298 299 # 第3优先级:天天基金网(直接爬取页面) 300 result = self._get_1234567fund_fund(fund_code) 301 if result["status"] == "success": 302 return result 303 304 # 所有数据源都失败 305 return { 306 "code": fund_code, 307 "name": "未知基金", 308 "current_value": "暂无数据", 309 "current_rate": "暂无数据", 310 "nav": "暂无数据", 311 "accum_nav": "暂无数据", 312 "day_rate": "暂无数据", 313 "time": "暂无数据", 314 "source": "无", 315 "status": "failed", 316 "error": "所有数据源均查询失败(可能被反爬/非交易时间)", 317 # 新增字段默认值 318 "yield_1day": "暂无数据", 319 "yield_1week": "暂无数据", 320 "yield_1month": "暂无数据", 321 "yield_3month": "暂无数据", 322 "yield_6month": "暂无数据", 323 "yield_1year": "暂无数据", 324 "yield_2year": "暂无数据", 325 "yield_3year": "暂无数据", 326 "yield_since_inception": "暂无数据", 327 "fund_type": "暂无数据", 328 "fund_company": "暂无数据" 329 } 330 331 def batch_get_fund_values(self, fund_codes): 332 """批量获取多只基金估值""" 333 results = [] 334 for code in fund_codes: 335 code = code.strip() 336 if code: # 跳过空值 337 result = self.get_fund_value(code) 338 results.append(result) 339 time.sleep(0.5) # 缩短间隔,同时降低反爬风险 340 return results 341 342 343 # 初始化基金估值工具 344 fund_tool = FundValuation() 345 346 347 # 路由:首页 348 @app.route("/", methods=["GET", "POST"]) 349 def index(): 350 if request.method == "POST": 351 # 获取用户输入的基金代码 352 fund_input = request.form.get("fund_codes", "") 353 # 分割多个基金代码(支持逗号、空格、换行、制表符) 354 fund_codes = re.split(r'[,,\s\n\t]+', fund_input) 355 fund_codes = [code for code in fund_codes if code.strip()] 356 357 # 批量查询基金估值 358 fund_results = fund_tool.batch_get_fund_values(fund_codes) 359 360 return render_template("index.html", 361 results=fund_results, 362 input_codes=fund_input) 363 364 # GET请求:展示空页面 365 return render_template("index.html", results=[], input_codes="") 366 367 368 # 路由:API接口(供前端异步调用) 369 @app.route("/api/fund", methods=["POST"]) 370 def api_fund(): 371 data = request.get_json() 372 fund_codes = data.get("fund_codes", []) 373 374 if not isinstance(fund_codes, list): 375 return jsonify({"status": "error", "message": "参数格式错误"}), 400 376 377 # 过滤空值 378 fund_codes = [code.strip() for code in fund_codes if code.strip()] 379 results = fund_tool.batch_get_fund_values(fund_codes) 380 return jsonify({"status": "success", "data": results}) 381 382 383 # 新增:单个基金查询API 384 @app.route("/api/fund/<fund_code>", methods=["GET"]) 385 def api_single_fund(fund_code): 386 """查询单只基金的详细信息""" 387 result = fund_tool.get_fund_value(fund_code) 388 if result["status"] == "success": 389 return jsonify({"status": "success", "data": result}) 390 else: 391 return jsonify({"status": "error", "message": result["error"]}), 400 392 393 394 if __name__ == "__main__": 395 # 关闭debug模式降低反爬风险,生产环境建议用False 396 app.run(host="0.0.0.0", port=5000, debug=False)
配套的 index.html 模板(templates/index.html)
为了展示新增的更多数据,需要创建 / 更新对应的 HTML 模板:
1 <!DOCTYPE html> 2 <html lang="zh-CN"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>基金估值查询工具</title> 7 <style> 8 * { 9 margin: 0; 10 padding: 0; 11 box-sizing: border-box; 12 } 13 body { 14 font-family: "Microsoft YaHei", Arial, sans-serif; 15 max-width: 1200px; 16 margin: 20px auto; 17 padding: 0 20px; 18 background-color: #f5f5f5; 19 } 20 .container { 21 background: white; 22 padding: 30px; 23 border-radius: 8px; 24 box-shadow: 0 2px 10px rgba(0,0,0,0.1); 25 } 26 h1 { 27 text-align: center; 28 color: #333; 29 margin-bottom: 30px; 30 } 31 .input-area { 32 margin-bottom: 20px; 33 } 34 textarea { 35 width: 100%; 36 padding: 10px; 37 border: 1px solid #ddd; 38 border-radius: 4px; 39 resize: vertical; 40 min-height: 100px; 41 font-size: 14px; 42 } 43 button { 44 background-color: #007bff; 45 color: white; 46 border: none; 47 padding: 10px 20px; 48 border-radius: 4px; 49 cursor: pointer; 50 font-size: 16px; 51 margin-top: 10px; 52 } 53 button:hover { 54 background-color: #0056b3; 55 } 56 .result-area { 57 margin-top: 30px; 58 } 59 table { 60 width: 100%; 61 border-collapse: collapse; 62 margin-top: 20px; 63 } 64 th, td { 65 padding: 12px 8px; 66 text-align: left; 67 border-bottom: 1px solid #ddd; 68 } 69 th { 70 background-color: #f8f9fa; 71 font-weight: bold; 72 color: #333; 73 } 74 tr:hover { 75 background-color: #f8f9fa; 76 } 77 .success { 78 color: green; 79 } 80 .failed { 81 color: red; 82 } 83 .rate-positive { 84 color: red; 85 } 86 .rate-negative { 87 color: green; 88 } 89 .note { 90 color: #666; 91 font-size: 12px; 92 margin-top: 10px; 93 } 94 .loading { 95 text-align: center; 96 padding: 20px; 97 color: #666; 98 } 99 </style> 100 </head> 101 <body> 102 <div class="container"> 103 <h1>基金估值查询工具</h1> 104 105 <div class="input-area"> 106 <form method="POST"> 107 <textarea name="fund_codes" placeholder="请输入基金代码,多个代码用逗号、空格或换行分隔(例如:000001 000002)">{{ input_codes }}</textarea> 108 <br> 109 <button type="submit">查询基金估值</button> 110 </form> 111 <p class="note">提示:支持5-6位数字的基金代码,数据来源包括1234567基金网、东方财富网、天天基金网</p> 112 </div> 113 114 <div class="result-area"> 115 {% if results %} 116 <table> 117 <thead> 118 <tr> 119 <th>基金代码</th> 120 <th>基金名称</th> 121 <th>实时估值</th> 122 <th>实时涨幅</th> 123 <th>单位净值</th> 124 <th>累计净值</th> 125 <th>日涨跌幅</th> 126 <th>近1周</th> 127 <th>近1月</th> 128 <th>近1年</th> 129 <th>成立以来</th> 130 <th>基金类型</th> 131 <th>基金公司</th> 132 <th>更新时间</th> 133 <th>数据来源</th> 134 <th>状态</th> 135 </tr> 136 </thead> 137 <tbody> 138 {% for fund in results %} 139 <tr> 140 <td>{{ fund.code }}</td> 141 <td>{{ fund.name }}</td> 142 <td>{{ fund.current_value }}</td> 143 <td class="{% if '-' in fund.current_rate %}rate-negative{% elif fund.current_rate != '0%' %}rate-positive{% endif %}"> 144 {{ fund.current_rate }} 145 </td> 146 <td>{{ fund.nav }}</td> 147 <td>{{ fund.accum_nav }}</td> 148 <td class="{% if '-' in fund.day_rate %}rate-negative{% elif fund.day_rate != '暂无数据' and fund.day_rate != '0%' %}rate-positive{% endif %}"> 149 {{ fund.day_rate }} 150 </td> 151 <td class="{% if '-' in fund.yield_1week %}rate-negative{% elif fund.yield_1week != '暂无数据' and fund.yield_1week != '0%' %}rate-positive{% endif %}"> 152 {{ fund.yield_1week }} 153 </td> 154 <td class="{% if '-' in fund.yield_1month %}rate-negative{% elif fund.yield_1month != '暂无数据' and fund.yield_1month != '0%' %}rate-positive{% endif %}"> 155 {{ fund.yield_1month }} 156 </td> 157 <td class="{% if '-' in fund.yield_1year %}rate-negative{% elif fund.yield_1year != '暂无数据' and fund.yield_1year != '0%' %}rate-positive{% endif %}"> 158 {{ fund.yield_1year }} 159 </td> 160 <td class="{% if '-' in fund.yield_since_inception %}rate-negative{% elif fund.yield_since_inception != '暂无数据' and fund.yield_since_inception != '0%' %}rate-positive{% endif %}"> 161 {{ fund.yield_since_inception }} 162 </td> 163 <td>{{ fund.fund_type }}</td> 164 <td>{{ fund.fund_company }}</td> 165 <td>{{ fund.time }}</td> 166 <td>{{ fund.source }}</td> 167 <td class="{% if fund.status == 'success' %}success{% else %}failed{% endif %}"> 168 {{ fund.status }} 169 </td> 170 </tr> 171 {% endfor %} 172 </tbody> 173 </table> 174 {% endif %} 175 </div> 176 </div> 177 </body> 178 </html>
功能升级说明
-
新增的数据字段:
- 单位净值(nav):基金的官方单位净值
- 累计净值(accum_nav):包含分红的累计净值
- 日涨跌幅(day_rate):当日官方涨跌幅
- 多维度收益率:近 1 日、近 1 周、近 1 月、近 3 月、近 6 月、近 1 年、近 2 年、近 3 年、成立以来
- 基金类型(fund_type):如股票型、混合型、债券型等
- 基金公司(fund_company):发行基金的公司名称
-
代码优化点:
- 增加了
_extract_numeric_value方法统一处理数值提取 - 优化了重试机制和超时设置(增加到 10 秒)
- 新增了单只基金查询的 API 接口
/api/fund/<fund_code> - 完善了异常处理和日志记录
- 优化了请求间隔(从 1 秒改为 0.5 秒)
- 增加了输入参数的空值过滤
- 增加了
-
前端展示优化:
- 表格展示所有新增数据字段
- 涨跌幅数值根据正负自动标红 / 标绿
- 优化了页面样式和用户体验
- 增加了鼠标悬停效果
部署和使用说明
-
环境准备:bash运行
# 安装依赖 pip install flask requests urllib3 -
目录结构:plaintext
fund_app/ ├── app.py # 主程序文件 └── templates/ # 模板文件夹 └── index.html # HTML模板文件 -
运行程序:bash运行
python app.py -
访问应用:
- 网页访问:http://localhost:5000
- API 调用示例(单只基金):http://localhost:5000/api/fund/000001
- API 调用示例(批量基金):POST http://localhost:5000/api/fund,JSON 参数:{"fund_codes": ["000001", "000002"]}
总结
- 核心升级:在原有仅展示估值和涨幅的基础上,新增了单位净值、累计净值、多维度收益率、基金类型、基金公司等 10 + 个数据维度,大幅提升了数据丰富度。
- 功能增强:优化了数据爬取逻辑,新增单基金查询 API,提升了接口的灵活性和可用性。
- 体验优化:配套的 HTML 模板支持新增数据的可视化展示,涨跌幅颜色区分,提升了用户体验。
注意:爬虫可能会受到网站反爬机制的限制,如果某数据源查询失败,程序会自动切换到备用数据源,确保服务的可用性。

浙公网安备 33010602011771号