#!/usr/bin/env python # -*- coding:utf-8 -*- """ Date: 2025/3/15 22:30 Desc: 港股股票指数数据-新浪-东财 所有指数-实时行情数据和历史行情数据 https://finance.sina.com.cn/realstock/company/sz399552/nc.shtml https://quote.eastmoney.com/gb/zsHSTECF2L.html """ import re import pandas as pd import requests import py_mini_racer from functools import lru_cache from akshare.stock.cons import hk_js_decode from akshare.utils.func import fetch_paginated_data def _replace_comma(x) -> str: """ 去除单元格中的 "," :param x: 单元格元素 :type x: str :return: 处理后的值或原值 :rtype: str """ if "," in str(x): return str(x).replace(",", "") else: return x def get_hk_index_page_count() -> int: """ 指数的总页数 https://vip.stock.finance.sina.com.cn/mkt/#zs_hk :return: 需要抓取的指数的总页数 :rtype: int """ res = requests.get( "https://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getNameCount?node=zs_hk" ) page_count = int(re.findall(re.compile(r"\d+"), res.text)[0]) / 80 if isinstance(page_count, int): return page_count else: return int(page_count) + 1 def stock_hk_index_spot_sina() -> pd.DataFrame: """ 新浪财经-行情中心-港股指数 大量采集会被目标网站服务器封禁 IP, 如果被封禁 IP, 请 10 分钟后再试 https://vip.stock.finance.sina.com.cn/mkt/#zs_hk :return: 所有指数的实时行情数据 :rtype: pandas.DataFrame """ url = ( "https://hq.sinajs.cn/rn=mtf2t&list=hkCES100,hkCES120,hkCES280,hkCES300,hkCESA80,hkCESG10," "hkCESHKM,hkCSCMC,hkCSHK100,hkCSHKDIV,hkCSHKLC,hkCSHKLRE,hkCSHKMCS,hkCSHKME,hkCSHKPE,hkCSHKSE," "hkCSI300,hkCSRHK50,hkGEM,hkHKL,hkHSCCI,hkHSCEI,hkHSI,hkHSMBI,hkHSMOGI,hkHSMPI,hkHSTECH,hkSSE180," "hkSSE180GV,hkSSE380,hkSSE50,hkSSECEQT,hkSSECOMP,hkSSEDIV,hkSSEITOP,hkSSEMCAP,hkSSEMEGA,hkVHSI" ) headers = {"Referer": "https://vip.stock.finance.sina.com.cn/"} r = requests.get(url, headers=headers) data_text = r.text data_list = [ item.split('"')[1].split(",") for item in data_text.split("\n") if len(item.split('"')) > 1 ] temp_df = pd.DataFrame(data_list) 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") return temp_df def stock_hk_index_daily_sina(symbol: str = "CES100") -> pd.DataFrame: """ 新浪财经-港股指数-历史行情数据 https://stock.finance.sina.com.cn/hkstock/quotes/CES100.html :param symbol: CES100, 港股指数代码 :type symbol: str :return: 历史行情数据 :rtype: pandas.DataFrame """ url = f"https://finance.sina.com.cn/stock/hkstock/{symbol}/klc2_kl.js" params = {"d": "2023_5_01"} res = requests.get(url, params=params) js_code = py_mini_racer.MiniRacer() js_code.eval(hk_js_decode) dict_list = js_code.call( "d", res.text.split("=")[1].split(";")[0].replace('"', "") ) # 执行js解密代码 temp_df = pd.DataFrame(dict_list) temp_df["date"] = pd.to_datetime(temp_df["date"], errors="coerce").dt.date temp_df["open"] = pd.to_numeric(temp_df["open"], errors="coerce") temp_df["close"] = pd.to_numeric(temp_df["close"], 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["volume"] = pd.to_numeric(temp_df["volume"], errors="coerce") return temp_df def stock_hk_index_spot_em() -> pd.DataFrame: """ 东方财富网-行情中心-港股-指数实时行情 https://quote.eastmoney.com/center/gridlist.html#hk_index :return: 指数行情 :rtype: pandas.DataFrame """ url = "https://15.push2.eastmoney.com/api/qt/clist/get" params = { "pn": "1", "pz": "100", "po": "1", "np": "1", "ut": "bd1d9ddb04089700cf9c27f6f7426281", "fltt": "2", "invt": "2", "wbp2u": "|0|0|0|web", "fid": "f3", "fs": "m:124,m:125,m:305", "fields": "f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25," "f26,f22,f33,f11,f62,f128,f136,f115,f152", } temp_df = fetch_paginated_data(url, params) temp_df.rename( columns={ "index": "序号", "f2": "最新价", "f3": "涨跌幅", "f4": "涨跌额", "f5": "成交量", "f6": "成交额", "f12": "代码", "f13": "内部编号", "f14": "名称", "f15": "最高", "f16": "最低", "f17": "今开", "f18": "昨收", }, inplace=True, ) 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") return temp_df @lru_cache() def _symbol_code_dict() -> dict: """ 缓存 ak.stock_hk_index_spot_em() 接口中的代码与内部编号 https://quote.eastmoney.com/center/gridlist.html#hk_index :return: 代码与内部编号 :rtype: dict """ __stock_hk_index_spot_em_df = stock_hk_index_spot_em() symbol_code_dict = dict( zip( __stock_hk_index_spot_em_df["代码"], __stock_hk_index_spot_em_df["内部编号"] ) ) return symbol_code_dict def stock_hk_index_daily_em(symbol: str = "HSTECF2L") -> pd.DataFrame: """ 东方财富网-港股-股票指数数据 https://quote.eastmoney.com/gb/zsHSTECF2L.html :param symbol: 港股指数代码; 可以通过 ak.stock_hk_index_spot_em() 获取 :type symbol: str :return: 指数数据 :rtype: pandas.DataFrame """ symbol_code_dict = _symbol_code_dict() symbol_code_dict.update( { "HSAHP": "100", } ) symbol_str = f"{symbol_code_dict[symbol]}.{symbol}" url = "https://push2his.eastmoney.com/api/qt/stock/kline/get" params = { "secid": symbol_str, "klt": "101", # 日频率 "fqt": "1", "lmt": "10000", "end": "20500000", "iscca": "1", "fields1": "f1,f2,f3,f4,f5,f6,f7,f8", "fields2": "f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64", "ut": "f057cbcbce2a86e2866ab8877db1d059", "forcect": "1", } r = requests.get(url, params=params) data_json = r.json() temp_df = pd.DataFrame([item.split(",") for item in data_json["data"]["klines"]]) temp_df.columns = [ "date", "open", "latest", "high", "low", "-", "-", "-", "-", "-", "-", "-", "-", "-", ] temp_df = temp_df[["date", "open", "high", "low", "latest"]] temp_df["open"] = pd.to_numeric(temp_df["open"], errors="coerce") temp_df["latest"] = pd.to_numeric(temp_df["latest"], errors="coerce") temp_df["high"] = pd.to_numeric(temp_df["high"], errors="coerce") temp_df["low"] = pd.to_numeric(temp_df["low"], errors="coerce") return temp_df if __name__ == "__main__": stock_hk_index_spot_sina_df = stock_hk_index_spot_sina() print(stock_hk_index_spot_sina_df) stock_hk_index_daily_sina_df = stock_hk_index_daily_sina(symbol="CES100") print(stock_hk_index_daily_sina_df) stock_hk_index_spot_em_df = stock_hk_index_spot_em() print(stock_hk_index_spot_em_df) stock_hk_index_daily_em_df = stock_hk_index_daily_em(symbol="HSTECH") print(stock_hk_index_daily_em_df)