Java量化系列(三十九) 某球分时数据抓取全攻略,分钟级数据+资金流向一键搞定

原本人链接是: Java量化系列(三十九): 某球分时数据抓取全攻略,分钟级数据+资金流向一键搞定

上一篇搞定了东方财富分时图数据,评论区炸了:"某球的分时数据怎么抓?""某球有资金流向细分,比东方财富更实用!""试了好几次,要么Cookie失效,要么编码报错,求手把手教程"🤯

确实,对于日内量化和实时复盘来说,某球分时数据有不可替代的优势------不仅包含分钟级价格波动,还自带小单/中单/大单/超大单资金流向细分,不用再额外拼接资金数据,大大提升策略开发效率。

但某球的接口反爬更严格、编码格式更特殊,Cookie失效、请求头配置错误、嵌套JSON解析翻车,都是量化开发者常踩的坑。

今天带来Java量化系列第39篇实战------某球股票分时图数据全流程落地方案,从编码转换、接口调用、嵌套JSON解析,到实体类封装、可视化对接,全套可运行代码直接奉上,全程聚焦技术实现,无任何投资引导,新手也能一键复用,彻底解决某球分时数据抓取难题。

一、核心认知:某球分时数据的优势与关键难点🎯

先明确某球分时数据的核心特点,避免走弯路,同时对比上一篇东方财富数据,帮你快速选型:

  • • 🔹 核心优势:分钟级价格数据(开盘/最高/最低/当前价)+ 资金流向细分(小单/中单/大单/超大单)+ 成交量/成交额统计,数据更全面,无需额外拼接。
  • • 🔹 数据来源:某球官方接口 https://stock.xueqiu.com/v5/stock/chart/minute.json,稳定性强,支持1天周期(period=1d)分钟级数据抓取。
  • • 🔹 核心难点:编码需转换为某球专属格式(沪市SH600000、深市SZ000001);接口需携带有效Cookie(反爬严格,Cookie失效直接返回403);返回数据为嵌套JSON,需多层解析才能提取核心字段。
  • • 🔹 与东方财富对比:某球胜在资金流向细分,东方财富胜在数据周期灵活,可根据自身策略需求选择(日内策略优先某球,多周期策略可搭配使用)。

关键提醒:某球接口的Cookie有效期较短,建议将Cookie存入Redis动态获取/更新,避免频繁手动替换,下文代码已内置Redis获取逻辑。

二、核心实现:从编码转换到数据解析全流程⚙️

整套方案分为"编码转换-接口调用-实体类封装-数据解析"四步,每一步都附完整代码+详细注释,所有代码可直接导入项目运行,无需额外修改。

2.1 编码转换:某球专属格式适配(核心第一步)

某球接口的symbol参数不支持原始股票代码,需转换为"市场前缀+股票代码"格式(沪市SH、深市SZ),同时兼容债券编码转换,核心工具方法如下:

复制代码
/**
 * 某球平台代码格式转换(适配某球symbol参数,格式为SH600000/SZ000001)
 *
 * @param code 原始股票/债券编码(如600000、000001、110088)
 * @return 某球标准格式代码(大写)
 */
public String convertXueQiuCode(String code) {
    // 若代码为空,或已为某球格式(长度≥7,如SH600000),直接返回(转大写避免大小写异常)
    if (StrUtil.isBlank(code) || code.length() >= 7) {
        return code.toUpperCase();
    }
    String validateCode = code;
    // 特殊处理债券:根据债券编码查询对应股票编码,再转换格式
    if (code.startsWith("1")) {
        KzzQueryParam kzzQueryParam = new KzzQueryParam();
        kzzQueryParam.setKzzCode(code);
        KzzDo kzzDo = kzzDomainService.getByCondition(kzzQueryParam);
        // 若未查询到对应股票编码,直接返回原始代码(避免转换失败)
        if (null == kzzDo) {
            return code;
        }
        validateCode = kzzDo.getStockCode();
    }
    // 按股票代码判断市场类型,拼接对应前缀(沪市SH,深市SZ)
    return StockCodeType.SH.equals(StockCodeType.getTypeByStockCode(validateCode)) 
            ? "SH" + code : "SZ" + code;
}

避坑重点:代码转换后需转大写,某球接口对symbol参数大小写敏感,小写会直接返回空数据;债券编码需先查询对应股票编码,再进行格式转换。

2.2 接口调用:请求头配置+Cookie处理+异常兜底

通过getTodayMinuteInfo方法调用某球分时接口,核心处理三点:请求头标准化(适配某球反爬)、Redis获取Cookie(动态更新)、熔断降级+异常兜底(避免服务雪崩),完整代码如下:

复制代码
/**
 * 从某球获取股票今日分钟级分时数据(含资金流向)
 *
 * @param code 原始股票/债券编码
 * @return 分钟级分时数据列表(TaosStockPrice60HistoryDo)
 */
public List<TaosStockPrice60HistoryDo> getTodayMinuteInfo(String code) {
    // 1. 转换股票编码为某球格式(适配symbol参数)
    String xueQiuCode = stockCodeHelper.convertXueQiuCode(code);
    // 2. 拼接接口地址(period=1d表示获取今日1天内分钟级数据)
    String url = MessageFormat.format("https://stock.xueqiu.com/v5/stock/chart/minute.json?symbol={0}&period=1d", xueQiuCode);
    // 3. 提取真实股票代码(去掉某球前缀SH/SZ,用于实体类赋值)
    String realCode = xueQiuCode.substring(2);
    
    try {
        // 4. 配置请求头(关键:某球反爬严格,请求头缺失会返回403)
        Map<String, String> header = new HashMap<>();
        header.put(":authority", "stock.xueqiu.com");
        header.put(":method", "GET");
        header.put(":path", "/v5/stock/chart/minute.json?symbol="+code+"&period=1d");
        header.put(":scheme", "https");
        header.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36");
        
        // 5. 从Redis获取某球Cookie(避免硬编码,动态更新,核心反爬处理)
        UserClientConfig userClientConfig = redisUtil.get(Const.USER_XUEQIU_CONFIG);
        if (StrUtil.isBlank(userClientConfig.getCookie())) {
            log.warn("某球Cookie为空,无法获取分时数据");
            return null;
        }
        header.put("Cookie", userClientConfig.getCookie());
        
        // 6. 发送GET请求(禁用代理,避免代理IP被限流)
        String content = HttpUtil.sendGet(HttpClientConfig.proxyNoUseCloseableHttpClient(), url, header);
        
        // 7. 解析返回内容,转换为实体类列表
        List<TaosStockPrice60HistoryDo> taosStockPrice60HistoryDos = xueQiuParser.parseXqStockMinuteIndex(content);
        
        // 8. 给实体类赋值真实股票代码(去掉某球前缀)
        if (CollUtil.isNotEmpty(taosStockPrice60HistoryDos)) {
            taosStockPrice60HistoryDos.forEach(n -> {
                n.setCode(realCode);
            });
        }
        return taosStockPrice60HistoryDos;
        
    } catch (Exception e) {
        log.error("IP:{} 获取股票{}分钟级数据出错,接口地址:{}", ThreadLocalUtils.getIp(), code, url, e);
        // 异常兜底:返回空列表,避免影响整体服务
        return Collections.emptyList();
    }
}

接口参数说明:某球分时接口核心参数简洁,无需复杂拼接,关键参数如下:

复制代码
// 某球分时接口模板({0}替换为转换后的某球编码,如SH600000)
https://stock.xueqiu.com/v5/stock/chart/minute.json?symbol={0}&period=1d
// 核心参数:
// symbol:某球标准编码(SH600000/SZ000001),必填
// period:数据周期,1d=今日、1w=近1周,此处用1d获取分时数据,必填

2.3 实体类封装:TaosStockPrice60HistoryDo(适配某球字段)

实体类严格对应某球接口返回的核心字段,包含价格数据、资金流向、成交量等所有信息,无需额外添加字段,直接用于数据存储和前端展示,完整代码如下:

复制代码
@Data
public class TaosStockPrice60HistoryDo implements Serializable {

    private static final long serialVersionUID = 1L;

    /** 主键编号(时间戳转换后的日期) */
    private Date ts;
    /** 股票编码(去掉某球SH/SZ前缀的真实编码) */
    private String code;

    /** 使用天(自定义字段,用于按天分组) */
    private Integer useDay;
    /** 使用时间(自定义字段,时分格式,如09:30) */
    private String useTime;
    
    /** 当前价格(分钟级实时价格) */
    private BigDecimal nowPrice;
    /** 最高价格(当日截至当前分钟的最高价) */
    private BigDecimal highestPrice;
    /** 最低价格(当日截至当前分钟的最低价) */
    private BigDecimal lowestPrice;
    /** 开盘价(当日开盘价) */
    private BigDecimal openingPrice;

    /** 涨跌幅度(绝对数值) */
    private BigDecimal amplitude;
    /** 涨跌幅度百分比(相对开盘价) */
    private BigDecimal amplitudeProportion;

    /** 单次成交量(当前分钟成交量) */
    private Long amount;
    /** 成交金额总数(当日截至当前分钟总成交额) */
    private Long amountTotal;
    /** 成交量总数(当日截至当前分钟总成交量) */
    private Long volumeTotal;

    /** 成交量总和(自定义统计字段) */
    private Long volumeSum;
    /** 成交上一层的总和(自定义统计字段) */
    private Long volumeSumLast;

    /** 小单资金流向(某球专属字段) */
    private BigDecimal small;
    /** 中单资金流向(某球专属字段) */
    private BigDecimal medium;
    /** 大单资金流向(某球专属字段) */
    private BigDecimal large;
    /** 超大单资金流向(某球专属字段) */
    private BigDecimal xlarge;

}

设计亮点:实体类兼顾"接口返回字段"和"业务需求",useDay、useTime为自定义字段,方便后续按天、按时分分组统计;资金流向字段(small/medium/large/xlarge)直接对应某球接口,无需额外解析拼接。

2.4 数据解析:嵌套JSON提取,规避解析翻车

某球接口返回的是嵌套JSON格式(data->items),items数组中每个元素包含单条分钟级数据,且资金流向、成交量统计为嵌套子JSON,核心解析逻辑如下:

复制代码
/**
 * 解析某球分时数据返回内容,转换为TaosStockPrice60HistoryDo列表
 *
 * @param content 某球接口返回的JSON字符串
 * @return 分钟级分时数据列表(含资金流向)
 */
@Override
public List<TaosStockPrice60HistoryDo> parseXqStockMinuteIndex(String content) {
    // 1. JSON字符串转JSON对象(容错:避免返回内容为空)
    JSONObject jsonObject = JSONObject.parseObject(content);
    // 2. 提取外层data字段(核心数据所在层级)
    JSONObject data = jsonObject.getJSONObject("data");
    if (ObjectUtils.isEmpty(data)) {
        log.warn("某球分时数据解析失败:data字段为空");
        return Collections.emptyList();
    }
    // 3. 提取items数组(分钟级数据列表,每个元素对应一条分钟数据)
    JSONArray jsonArray = data.getJSONArray("items");
    if (jsonArray.size() <= 0) {
        log.warn("某球分时数据解析失败:items数组为空");
        return Collections.emptyList();
    }
    // 4. 循环解析每条分钟数据,封装为实体类
    List<TaosStockPrice60HistoryDo&gt; result = new ArrayList<>(6);
    jsonArray.forEach(n -> {
        // 转换为JSON对象(处理数组中元素为字符串的异常)
        JSONObject tempObject = JSONObject.parseObject(n.toString());
        
        TaosStockPrice60HistoryDo taosStockPrice60HistoryDo = new TaosStockPrice60HistoryDo();
        
        // 处理时间字段:时间戳转换为日期、时分格式
        Long timestamp = tempObject.getLong("timestamp");
        Date timeStampDate = new Date(timestamp);
        taosStockPrice60HistoryDo.setUseDay(MyDateUtil.getUseDay(timeStampDate));
        taosStockPrice60HistoryDo.setUseTime(MyDateUtil.getUseTime(timeStampDate));
        
        // 价格相关字段(直接提取,对应接口current/high/low等字段)
        taosStockPrice60HistoryDo.setNowPrice(tempObject.getBigDecimal("current"));
        taosStockPrice60HistoryDo.setHighestPrice(tempObject.getBigDecimal("high"));
        taosStockPrice60HistoryDo.setLowestPrice(tempObject.getBigDecimal("low"));
        taosStockPrice60HistoryDo.setAmplitude(tempObject.getBigDecimal("chg"));
        taosStockPrice60HistoryDo.setAmplitudeProportion(tempObject.getBigDecimal("percent"));
        
        // 成交量、成交额相关字段
        taosStockPrice60HistoryDo.setAmount(tempObject.getLong("amount"));
        taosStockPrice60HistoryDo.setAmountTotal(tempObject.getLong("amount_total"));
        taosStockPrice60HistoryDo.setVolumeTotal(tempObject.getLong("volume_total"));
        
        // 解析嵌套子JSON:volume_sum(成交量总和相关)
        JSONObject volumeSumObject = tempObject.getJSONObject("volume_sum");
        if (volumeSumObject != null) {
            taosStockPrice60HistoryDo.setVolumeSum(volumeSumObject.getLong("volume_sum"));
            taosStockPrice60HistoryDo.setVolumeSumLast(volumeSumObject.getLong("volume_sum_last"));
        }
        
        // 解析嵌套子JSON:capital(资金流向相关,某球专属)
        JSONObject capitalObject = tempObject.getJSONObject("capital");
        if (capitalObject != null) {
            taosStockPrice60HistoryDo.setSmall(capitalObject.getBigDecimal("small"));
            taosStockPrice60HistoryDo.setMedium(capitalObject.getBigDecimal("medium"));
            taosStockPrice60HistoryDo.setLarge(capitalObject.getBigDecimal("large"));
            taosStockPrice60HistoryDo.setXlarge(capitalObject.getBigDecimal("xlarge"));
        }
        
        result.add(taosStockPrice60HistoryDo);
    });
    return result;
}

解析避坑:必须判断嵌套子JSON(volume_sum、capital)是否为空,避免空指针异常;时间戳转换时直接使用接口返回的timestamp字段,无需额外格式转换,提升解析效率。

三、可视化落地:分时图+资金流向双重可视化📊

解析后的某球分时数据(含资金流向)可直接对接ECharts,实现"分时价格走势+资金流向分布"双重可视化,适配日内复盘和策略分析,核心对接代码如下(直接复用可运行):

3.1 分时价格走势图(延续上一篇风格,适配某球数据)

以折线图展示分钟级价格波动,标注开盘价、最高价、最低价,结合涨跌幅度,直观呈现日内价格走势:

复制代码
// 前端ECharts配置(某球分时价格走势)
const xueQiuTimeSharingData = taosStockPrice60HistoryDos; // 后端返回的某球分时数据列表
option = {
    title: { text: `${xueQiuTimeSharingData[0].code} 某球分时价格走势`, left: 'center' },
    tooltip: {
        trigger: 'axis',
        formatter: function(params) {
            const data = params[0].data;
            return `时间:${data[0]}<br/>当前价:${data[1]}元<br/>涨幅:${data[2]}%<br/>成交额:${data[3]}元`;
        }
    },
    xAxis: {
        type: 'category',
        data: xueQiuTimeSharingData.map(item => item.useTime), // 直接使用自定义useTime字段(时分)
        axisLabel: { interval: 10 } // 每10个数据显示一个标签,避免拥挤
    },
    yAxis: { type: 'value', name: '价格(元)' },
    series: [{
        name: '分时价格',
        type: 'line',
        data: xueQiuTimeSharingData.map(item => [
            item.useTime,
            item.nowPrice,
            item.amplitudeProportion,
            item.amountTotal
        ]),
        lineStyle: { color: '#ef232a', width: 2 },
        markPoint: {
            data: [
                { name: '最高价', value: '最高价', yAxis: Math.max(...xueQiuTimeSharingData.map(item => item.highestPrice)) },
                { name: '最低价', value: '最低价', yAxis: Math.min(...xueQiuTimeSharingData.map(item => item.lowestPrice)) }
            ]
        }
    }]
};

3.2 资金流向分布图(某球专属,核心亮点)

利用某球专属的资金流向字段,以堆叠柱状图展示每分钟小单/中单/大单/超大单分布,辅助分析日内资金动向:

复制代码
// 前端ECharts配置(某球分时资金流向)
option = {
    title: { text: `${xueQiuTimeSharingData[0].code} 分时资金流向`, left: 'center' },
    tooltip: { trigger: 'axis', stacked: true },
    xAxis: {
        type: 'category',
        data: xueQiuTimeSharingData.map(item => item.useTime),
        axisLabel: { interval: 10 }
    },
    yAxis: { type: 'value', name: '资金流向(元)' },
    legend: { data: ['小单', '中单', '大单', '超大单'], bottom: 0 },
    series: [
        { name: '小单', type: 'bar', stack: 'total', data: xueQiuTimeSharingData.map(item => item.small) },
        { name: '中单', type: 'bar', stack: 'total', data: xueQiuTimeSharingData.map(item => item.medium) },
        { name: '大单', type: 'bar', stack: 'total', data: xueQiuTimeSharingData.map(item => item.large) },
        { name: '超大单', type: 'bar', stack: 'total', data: xueQiuTimeSharingData.map(item => item.xlarge) }
    ]
};

可视化效果说明:堆叠柱状图可直观看到每分钟不同量级资金的流入流出情况,红色代表流入、绿色代表流出(ECharts可自行配置颜色),完美适配日内资金分析场景。

四、避坑指南:7个某球抓取实战踩雷教训⚠️(必看)

某球接口反爬比东方财富更严格,实战中踩了很多坑,整理了7个高频问题,帮你一次性规避,节省调试时间:

坑1:编码未转换或大小写错误,接口返回空数据

✅ 原因:直接用原始代码(如600000)作为symbol参数,或转换后为小写(sh600000),某球接口无法识别。

✅ 解决:必须通过convertXueQiuCode方法转换,且确保编码为大写(SH600000/SZ000001)。

坑2:Cookie缺失或失效,返回403 Forbidden

✅ 原因:某球反爬严格,未携带Cookie或Cookie失效,被识别为爬虫,拒绝访问。

✅ 解决:将某球Cookie存入Redis,定时更新;代码中添加Cookie非空判断,避免空Cookie请求。

坑3:请求头配置不完整,接口返回乱码或空数据

✅ 原因:缺失:authority:scheme等请求头字段,被某球反爬机制拦截。

✅ 解决:严格按照代码中请求头配置,不要遗漏任何字段,User-Agent使用主流浏览器标识。

坑4:嵌套JSON解析未判空,报空指针异常

✅ 原因:volume_sum、capital字段可能为空,未判断直接解析,导致空指针异常。

✅ 解决:解析嵌套子JSON前,先判断是否为空,非空再提取字段。

坑5:使用代理IP,被某球限流

✅ 原因:某球对代理IP管控严格,频繁使用代理IP请求,会被限制访问。

✅ 解决:禁用代理(代码中已配置proxyNoUseCloseableHttpClient),使用本机IP请求。

坑6:实体类字段类型不匹配,解析失败

✅ 原因:某球接口返回的amount、amount_total等字段为Long类型,实体类误用为Integer,导致类型转换异常。

✅ 解决:严格按照TaosStockPrice60HistoryDo实体类定义,价格字段用BigDecimal,数量字段用Long。

坑7:未做异常兜底,接口报错拖垮整个服务

✅ 原因:接口超时、解析失败等异常未处理,导致线程阻塞,拖垮量化系统。

✅ 解决:添加try-catch异常捕获,日志打印详细错误信息,异常时返回空列表,避免服务雪崩。

五、福利领取:完整可运行代码包免费送🎁

为了帮大家快速落地,我整理了本次某球分时数据实战的完整可运行代码包,包含:

  • • ① 编码转换、接口调用、数据解析完整代码(可直接复制复用);
  • • ② TaosStockPrice60HistoryDo实体类+常量定义;
  • • ③ 前端ECharts分时图+资金流向可视化完整配置;
  • • ④ 某球Cookie获取+Redis存储教程;
  • • ⑤ 避坑指南完整版(含常见错误调试方法)。

私信回复【某球分时数据】,即可免费领取!所有代码均可直接导入项目运行,无需修改,帮你节省1-2天开发时间,彻底搞定某球分时数据抓取。

下期预告✨

上两篇我们分别搞定了东方财富、某球的分时数据抓取,下期将聚焦 可交易日期,即代码中常常出现的 DateHelper 类,敬请期待!

结尾互动

你在抓取某球分时数据时,还遇到过哪些反爬难题?Cookie经常失效?资金流向解析翻车?需要我补充Cookie自动更新的实现逻辑吗?欢迎在评论区留言讨论!

如果觉得这篇文章对你有帮助,别忘了点赞+在看+转发,让更多量化开发者告别某球数据的困扰~ 关注我,持续解锁Java量化实战干货!🚀

原本人链接是: Java量化系列(三十九): 某球分时数据抓取全攻略,分钟级数据+资金流向一键搞定

相关推荐
pchaoda6 天前
RSI与布林带技术指标实战
python·matplotlib·量化
pchaoda7 天前
基本面因子计算入门
python·matplotlib·量化
两个蝴蝶飞7 天前
Java 量化系列(三十三):全自动维护股票与板块 概念 地域关联关系,效率拉满
股票量化·量化·java量化·java指标可视化
gzxx2007sddx11 天前
windows vnpy运行过程及问题记录
python·量化·vnpy
程序员羽毛14 天前
🚀 股票量化多策略盯盘哨兵 V3.0.0 涨停板监控+回测+回放+摸鱼全搞定
股票量化·监控·提醒·股票策略
skywalk816323 天前
介绍一下 Backtrader量化框架(C# 回测快)
开发语言·c#·量化
skywalk816323 天前
介绍一下QuantConnect Lean(python 15k star)
开发语言·python·量化
二狗哈1 个月前
czsc入门10: Position类
量化·czsc
二狗哈1 个月前
czsc入门8:Signal信号
python·量化·czsc