fa45d8aa5f
- health_checklist.json: 192.168.1.122→node122
- ocr_client.py: docstring IP→node122
- docs/market-data-requirements.md: IP→node122
- 所有API调用通过ProxyHandler({})绕过系统代理
Privoxy对node122:18003返回500,直连正常
206 lines
6.9 KiB
Python
206 lines
6.9 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding:utf-8 -*-
|
|
"""
|
|
Date: 2025/11/10 15:30
|
|
Desc: 新浪财经-基金行情
|
|
https://vip.stock.finance.sina.com.cn/fund_center/index.html#jjhqetf
|
|
"""
|
|
|
|
import pandas as pd
|
|
import py_mini_racer
|
|
import requests
|
|
|
|
from akshare.stock.cons import hk_js_decode
|
|
from akshare.utils import demjson
|
|
|
|
|
|
def fund_etf_category_sina(symbol: str = "LOF基金") -> pd.DataFrame:
|
|
"""
|
|
新浪财经-基金列表
|
|
https://vip.stock.finance.sina.com.cn/fund_center/index.html#jjhqetf
|
|
:param symbol: choice of {"封闭式基金", "ETF基金", "LOF基金"}
|
|
:type symbol: str
|
|
:return: 指定 symbol 的基金列表
|
|
:rtype: pandas.DataFrame
|
|
"""
|
|
fund_map = {
|
|
"封闭式基金": "close_fund",
|
|
"ETF基金": "etf_hq_fund",
|
|
"LOF基金": "lof_hq_fund",
|
|
}
|
|
url = (
|
|
"https://vip.stock.finance.sina.com.cn/quotes_service/api/jsonp.php/"
|
|
"IO.XSRV2.CallbackList['da_yPT46_Ll7K6WD']/Market_Center.getHQNodeDataSimple"
|
|
)
|
|
params = {
|
|
"page": "1",
|
|
"num": "5000",
|
|
"sort": "symbol",
|
|
"asc": "0",
|
|
"node": fund_map[symbol],
|
|
"[object HTMLDivElement]": "qvvne",
|
|
}
|
|
r = requests.get(url, params=params)
|
|
data_text = r.text
|
|
data_json = demjson.decode(data_text[data_text.find("([") + 1 : -2])
|
|
temp_df = pd.DataFrame(data_json)
|
|
if symbol == "封闭式基金":
|
|
temp_df.columns = [
|
|
"代码",
|
|
"名称",
|
|
"最新价",
|
|
"涨跌额",
|
|
"涨跌幅",
|
|
"买入",
|
|
"卖出",
|
|
"昨收",
|
|
"今开",
|
|
"最高",
|
|
"最低",
|
|
"成交量",
|
|
"成交额",
|
|
"_",
|
|
"_",
|
|
]
|
|
else:
|
|
temp_df.columns = [
|
|
"代码",
|
|
"名称",
|
|
"最新价",
|
|
"涨跌额",
|
|
"涨跌幅",
|
|
"买入",
|
|
"卖出",
|
|
"昨收",
|
|
"今开",
|
|
"最高",
|
|
"最低",
|
|
"成交量",
|
|
"成交额",
|
|
"_",
|
|
"_",
|
|
"_",
|
|
"_",
|
|
]
|
|
temp_df = temp_df[
|
|
[
|
|
"代码",
|
|
"名称",
|
|
"最新价",
|
|
"涨跌额",
|
|
"涨跌幅",
|
|
"买入",
|
|
"卖出",
|
|
"昨收",
|
|
"今开",
|
|
"最高",
|
|
"最低",
|
|
"成交量",
|
|
"成交额",
|
|
]
|
|
]
|
|
temp_df["最新价"] = pd.to_numeric(temp_df["最新价"], errors="coerce")
|
|
temp_df["涨跌额"] = pd.to_numeric(temp_df["涨跌额"], errors="coerce")
|
|
temp_df["涨跌幅"] = pd.to_numeric(temp_df["涨跌幅"], errors="coerce")
|
|
temp_df["买入"] = pd.to_numeric(temp_df["买入"], errors="coerce")
|
|
temp_df["卖出"] = pd.to_numeric(temp_df["卖出"], errors="coerce")
|
|
temp_df["昨收"] = pd.to_numeric(temp_df["昨收"], errors="coerce")
|
|
temp_df["今开"] = pd.to_numeric(temp_df["今开"], errors="coerce")
|
|
temp_df["最高"] = pd.to_numeric(temp_df["最高"], errors="coerce")
|
|
temp_df["最低"] = pd.to_numeric(temp_df["最低"], errors="coerce")
|
|
temp_df["成交量"] = pd.to_numeric(temp_df["成交量"], errors="coerce")
|
|
temp_df["成交额"] = pd.to_numeric(temp_df["成交额"], errors="coerce")
|
|
return temp_df
|
|
|
|
|
|
def fund_etf_hist_sina(symbol: str = "sh510050") -> pd.DataFrame:
|
|
"""
|
|
新浪财经-基金-ETF 基金-日行情数据
|
|
https://finance.sina.com.cn/fund/quotes/159996/bc.shtml
|
|
:param symbol: 基金名称, 可以通过 ak.fund_etf_category_sina() 函数获取
|
|
:type symbol: str
|
|
:return: 日行情数据
|
|
:rtype: pandas.DataFrame
|
|
"""
|
|
url = (
|
|
f"https://finance.sina.com.cn/realstock/company/{symbol}/hisdata_klc2/klc_kl.js"
|
|
)
|
|
r = requests.get(url)
|
|
js_code = py_mini_racer.MiniRacer()
|
|
js_code.eval(hk_js_decode)
|
|
dict_list = js_code.call(
|
|
"d", r.text.split("=")[1].split(";")[0].replace('"', "")
|
|
) # 执行js解密代码
|
|
temp_df = pd.DataFrame(dict_list)
|
|
if temp_df.empty: # 处理获取数据为空的问题
|
|
return pd.DataFrame()
|
|
temp_df["date"] = pd.to_datetime(temp_df["date"], errors="coerce").dt.tz_localize(
|
|
None
|
|
)
|
|
temp_df["open"] = pd.to_numeric(temp_df["open"], errors="coerce")
|
|
temp_df["high"] = pd.to_numeric(temp_df["high"], errors="coerce")
|
|
temp_df["low"] = pd.to_numeric(temp_df["low"], errors="coerce")
|
|
temp_df["close"] = pd.to_numeric(temp_df["close"], errors="coerce")
|
|
temp_df["volume"] = pd.to_numeric(temp_df["volume"], errors="coerce")
|
|
|
|
# 转换日期列为日期类型
|
|
temp_df["date"] = temp_df["date"].dt.date
|
|
temp_df = temp_df.sort_values(by="date", ascending=True)
|
|
return temp_df
|
|
|
|
|
|
def fund_etf_dividend_sina(symbol: str = "sh510050") -> pd.DataFrame:
|
|
"""
|
|
新浪财经-基金-ETF 基金-累计分红
|
|
https://finance.sina.com.cn/fund/quotes/510050/bc.shtml
|
|
:param symbol: 基金名称, 可以通过 ak.fund_etf_category_sina() 函数获取
|
|
:type symbol: str
|
|
:return: 累计分红
|
|
:rtype: pandas.DataFrame
|
|
"""
|
|
# 构建复权数据URL
|
|
factor_url = f"https://finance.sina.com.cn/realstock/company/{symbol}/hfq.js"
|
|
r = requests.get(factor_url)
|
|
text = r.text
|
|
if text.startswith("var"):
|
|
json_str = text.split("=")[1].strip().rsplit("}", maxsplit=1)[0].strip()
|
|
data = eval(json_str + "}") # 这里使用eval而不是json.loads因为数据格式特殊
|
|
|
|
if isinstance(data, dict) and "data" in data:
|
|
df = pd.DataFrame(data["data"])
|
|
# 重命名列
|
|
df.columns = ["date", "f", "s", "u"] if len(df.columns) == 4 else df.columns
|
|
# 移除1900-01-01的数据
|
|
df = df[df["date"] != "1900-01-01"]
|
|
# 转换日期
|
|
df["date"] = pd.to_datetime(df["date"])
|
|
# 转换数值类型
|
|
df[["f", "s", "u"]] = df[["f", "s", "u"]].astype(float)
|
|
# 按日期排序
|
|
df = df.sort_values(by="date", ascending=True, ignore_index=True)
|
|
temp_df = df[["date", "u"]].copy()
|
|
temp_df.columns = ["日期", "累计分红"]
|
|
temp_df["日期"] = pd.to_datetime(temp_df["日期"], errors="coerce").dt.date
|
|
return temp_df
|
|
else:
|
|
return pd.DataFrame()
|
|
else:
|
|
return pd.DataFrame()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
fund_etf_category_sina_df = fund_etf_category_sina(symbol="封闭式基金")
|
|
print(fund_etf_category_sina_df)
|
|
|
|
fund_etf_category_sina_df = fund_etf_category_sina(symbol="ETF基金")
|
|
print(fund_etf_category_sina_df)
|
|
|
|
fund_etf_category_sina_df = fund_etf_category_sina(symbol="LOF基金")
|
|
print(fund_etf_category_sina_df)
|
|
|
|
fund_etf_hist_sina_df = fund_etf_hist_sina(symbol="sh510050")
|
|
print(fund_etf_hist_sina_df)
|
|
|
|
fund_etf_dividend_sina_df = fund_etf_dividend_sina(symbol="sh510050")
|
|
print(fund_etf_dividend_sina_df)
|