使用RestTemplate获取国内大盘股票数据的基本信息并存入数据库中

目录

使用RestTemplate获取国内大盘股票数据的基本信息并存入数据库中

第一步:导入RestTemplate依赖,并配置RestTemplate让其加入到SpringIoC容器中

第二步:在yml文件定义股票的相关参数

第三步:向新浪网发送请求,获取国内大盘股票的JS格式数据

第四步:使用正则表达式解析JS数据格式,并把解析后的数据封装到entity对象中

第五步:使用mybatis把entities集合批量入库

 mapper

mapperXml文件

第六步:查看数据库是否插入成功

把JS数据封装成entity类对象的功能抽取出来,成为一个工具类

在配置类中把工具类交给bean管理,并给成员变量进行依赖注入

使用RestTemplate获取国内A股股票的基本信息,并存入数据库

返回的个股JS数据

查询思路:我们把查询到的A股个股股票编码进行分组,15个股票编码一组,使用foreach方法进行循环

查询所有个股股票编码实现

总体业务实现 

 mapper

mapperXml

结果:​编辑

使用RestTemplate获取国内板块的信息,并存入数据库

 返回的JS数据

解析思路:

 gson导入

示例

把JS格式数据转换成List集合工具类编写

业务实现

mapper

mapperXml


使用RestTemplate获取国内大盘股票数据的基本信息并存入数据库中

基本流程:

使用RestTemplate向sina网发送请求,并获取返回的JS数据格式

var hq_str_sh000001="上证指数,3358.9338,3361.5177,3398.6161,3417.0085,3358.9338,0,0,381243178,510307202948,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2022-06-30,15:30:39,00,";
参数说明:
0:指数名称
1:开盘点
2:前收盘点
3:当前点
4:最高点
5:最低点
8:成交量
9:成交金额
30:当前日期
31:当前时间

获取到JS数据后,使用正则表达式解析JS数据,并把解析后的数据封装到entity对象中,在把entity对象插入到数据库中 

第一步:导入RestTemplate依赖,并配置RestTemplate让其加入到SpringIoC容器中

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
/**
 * 定义http客户端工具bean
 */
@Configuration
public class HttpClientConfig {
    /**
     * 定义http客户端工具bean
     */
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

第二步:在yml文件定义股票的相关参数

# 配置股票相关的参数
stock:
  inner:   #A股
    - sh000001 # 上证ID
    - sz399001 #  深证ID
  outer: # 外盘
    - int_dji # 道琼斯
    - int_nasdaq # 纳斯达克
    - int_hangseng # 恒生
    - int_nikkei # 日经指数
    - b_FSSTI # 新加坡
  marketUrl: https://hq.sinajs.cn/list=
  blockUrl: http://vip.stock.finance.sina.com.cn/q/view/newSinaHy.php

在value object 包下写一个与yml文件相互映射的类

/**
 * 大盘编码信息
 */
@Data
@ConfigurationProperties(prefix = "stock")//映射yml文件中stock属性的值
//@Component//直接让其交给spring管理,TODO:我们不使用(不让它自己开启),我们让其他要使用该类的子模块来开启
public class StockInfoConfig {
    private List<String>inner;//国内大盘编码
    private List<String>outer;//国外大盘编码
    private String marketUrl;//大盘 外盘 个股的公共Url请求路径
    private String blockUrl;//板块采集url请求地址
}

开启这个与yml文件相互映射的类,并交给spring管理

@Configuration
//开启StockInfoConfig这个类,开始让这个类映射加载yml文件的属性,并交给spring管理
@EnableConfigurationProperties(StockInfoConfig.class)
public class CommonConfig {
    //配置雪花算法的工具类bean,交给spring管理
    @Bean
    public IdWorker idWorker(){
        /**
         * 参数一:机器ID
         * 参数二:机房ID
         */
        return new IdWorker(1L,2L);
    }
}

第三步:向新浪网发送请求,获取国内大盘股票的JS格式数据

@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Override
    public void getInnerMarketInfo() {
        //TODO:1.从新浪网获取国内大盘的JS数据格式
        //设置请求路径
        //https://hq.sinajs.cn/list=sh000001,sz399001
        String url=stockInfoConfig.getMarketUrl()+String.join(",",stockInfoConfig.getInner());
        //join方法可以把集合内的数据装换成String数据类型,并使用,分隔
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        //必须填写,否则数据采集不到
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        //组装请求对象
        HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
        //使用restTemplate发送请求数据
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //获取响应码
        int statusCode= responseEntity.getStatusCodeValue();
        if(statusCode!=200){
            log.error("时间:{},采集数据出错,响应码状态为:{}", DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),statusCode);
            return;
        }
        //获取js响应数据
        String resData = responseEntity.getBody();
        log.info("时间:{},采集数据成功,数据为:{}",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),resData);
        /**
         * 结果:
         * 时间:2024-09-06 20:05:04,采集数据成功,数据为:
         * var hq_str_sh000001="上证指数,2791.7645,2788.3141,2765.8066,2804.0932,2765.6394,0,0,253645375,228570135027,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2024-09-06,15:30:39,00,";
         * var hq_str_sz399001="深证成指,8252.006,8249.659,8130.770,8260.690,8128.767,0.000,0.000,36378777179,314062276972.566,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,2024-09-06,15:00:03,00";
         */
    }
}

第四步:使用正则表达式解析JS数据格式,并把解析后的数据封装到entity对象中

entity类对象(直接与数据库字段值进行交互)

/**
 * 国内大盘数据详情表
 * @TableName stock_market_index_info
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StockMarketIndexInfo implements Serializable {
    /**
     * 主键字段(无业务意义)
     */
    private Long id;

    /**
     * 大盘编码
     */
    private String marketCode;

    /**
     * 指数名称
     */
    private String marketName;

    /**
     * 前收盘点数
     */
    private BigDecimal preClosePoint;

    /**
     * 开盘点数
     */
    private BigDecimal openPoint;

    /**
     * 当前点数
     */
    private BigDecimal curPoint;

    /**
     * 最低点数
     */
    private BigDecimal minPoint;

    /**
     * 最高点数
     */
    private BigDecimal maxPoint;

    /**
     * 成交量(手)
     */
    private Long tradeAmount;

    /**
     * 成交金额(元)
     */
    private BigDecimal tradeVolume;

    /**
     * 当前时间
     */
    private Date curTime;

    private static final long serialVersionUID = 1L;
}
@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Override
    public void getInnerMarketInfo() {
        //TODO:1.从新浪网获取国内大盘的JS数据格式
        //设置请求路径
        //https://hq.sinajs.cn/list=sh000001,sz399001
        String url=stockInfoConfig.getMarketUrl()+String.join(",",stockInfoConfig.getInner());
        //join方法可以把集合内的数据装换成String数据类型,并使用,分隔
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        //必须填写,否则数据采集不到
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        //组装请求对象
        HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
        //使用restTemplate发送请求数据
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //获取响应码
        int statusCode= responseEntity.getStatusCodeValue();
        if(statusCode!=200){
            log.error("时间:{},采集数据出错,响应码状态为:{}", DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),statusCode);
            return;
        }
        //获取js响应数据
        String resData = responseEntity.getBody();
        log.info("时间:{},采集数据成功,数据为:{}",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),resData);
        /**
         * 结果:
         * 时间:2024-09-06 20:05:04,采集数据成功,数据为:
         * var hq_str_sh000001="上证指数,2791.7645,2788.3141,2765.8066,2804.0932,2765.6394,0,0,253645375,228570135027,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2024-09-06,15:30:39,00,";
         * var hq_str_sz399001="深证成指,8252.006,8249.659,8130.770,8260.690,8128.767,0.000,0.000,36378777179,314062276972.566,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,2024-09-06,15:00:03,00";
         */

        //2.TODO:使用正则解析JS数据
        //定义正则表达式
        String regex="hq_str_(.+)=\"(.+)\";";
        //表达式编译
        Pattern pattern = Pattern.compile(regex);
        //匹配字符串
        Matcher matcher = pattern.matcher(resData);
        List<StockMarketIndexInfo>entities=new ArrayList<>();
        while (matcher.find()){//有多行数据(以 ; 结尾),要使用while循环
            //如果使用的是group(0),则获取匹配的全部数据
            //获取大盘的编码
            String marketCode = matcher.group(1);//获取第一个括号匹配的数据
            //获取大盘的其他信息
            String otherInfo=matcher.group(2);//获取第二个括号匹配的数据
            //分割其他信息成String数据
            String[] splitArr = otherInfo.split(",");
            //大盘名称
            String marketName=splitArr[0];
            //获取当前大盘的开盘点数
            BigDecimal openPoint=new BigDecimal(splitArr[1]);//直接把String类型的数据变成BigDecimal数据类型
            //前收盘点
            BigDecimal preClosePoint=new BigDecimal(splitArr[2]);
            //获取大盘的当前点数
            BigDecimal curPoint=new BigDecimal(splitArr[3]);
            //获取大盘最高点
            BigDecimal maxPoint=new BigDecimal(splitArr[4]);
            //获取大盘的最低点
            BigDecimal minPoint=new BigDecimal(splitArr[5]);
            //获取成交量
            Long tradeAmt=Long.valueOf(splitArr[8]);
            //获取成交金额
            BigDecimal tradeVol=new BigDecimal(splitArr[9]);
            //获取交易时间
            DateTime dateTime = DateTime.parse(splitArr[30] + " " + splitArr[31], DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
            Date date = dateTime.withSecondOfMinute(0).withMillisOfSecond(0).toDate();

            //TODO:3.把获取的数据封装成entity对象,用来插入数据库
            StockMarketIndexInfo entity = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId())//使用雪花算法生成一个Long类型的id
                    .marketCode(marketCode)
                    .marketName(marketName)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .curPoint(curPoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeAmount(tradeAmt)
                    .tradeVolume(tradeVol)
                    .curTime(date).build();
            entities.add(entity);
        }
        log.info("当前时间:{},获取的数据为:{}",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-ss HH:mm:ss")),entities);
        /**
         * 当前时间:2024-09-14 20:52:14,获取的数据为:
         * [
         * StockMarketIndexInfo(id=1832039060873678848, marketCode=sh000001, marketName=上证指数, preClosePoint=2788.3141, openPoint=2791.7645, curPoint=2765.8066, minPoint=2765.6394, maxPoint=2804.0932, tradeAmount=253645375, tradeVolume=228570135027, curTime=Fri Sep 06 15:30:00 CST 2024),
         * StockMarketIndexInfo(id=1832039103492001792, marketCode=sz399001, marketName=深证成指, preClosePoint=8249.659, openPoint=8252.006, curPoint=8130.770, minPoint=8128.767, maxPoint=8260.690, tradeAmount=36378777179, tradeVolume=314062276972.566, curTime=Fri Sep 06 15:00:00 CST 2024)
         * ]
     
    }
}

第五步:使用mybatis把entities集合批量入库

import com.hhh.stock.mapper.StockMarketIndexInfoMapper;
import com.hhh.stock.pojo.entity.StockMarketIndexInfo;
import com.hhh.stock.pojo.vo.StockInfoConfig;
import com.hhh.stock.service.StockTimerTaskService;
import com.hhh.stock.utils.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Override
    public void getInnerMarketInfo() {
        //TODO:1.从新浪网获取国内大盘的JS数据格式
        //设置请求路径
        //https://hq.sinajs.cn/list=sh000001,sz399001
        String url=stockInfoConfig.getMarketUrl()+String.join(",",stockInfoConfig.getInner());
        //join方法可以把集合内的数据装换成String数据类型,并使用,分隔
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        //必须填写,否则数据采集不到
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        //组装请求对象
        HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
        //使用restTemplate发送请求数据
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //获取响应码
        int statusCode= responseEntity.getStatusCodeValue();
        if(statusCode!=200){
            log.error("时间:{},采集数据出错,响应码状态为:{}", DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),statusCode);
            return;
        }
        //获取js响应数据
        String resData = responseEntity.getBody();
        log.info("时间:{},采集数据成功,数据为:{}",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),resData);
        /**
         * 结果:
         * 时间:2024-09-06 20:05:04,采集数据成功,数据为:
         * var hq_str_sh000001="上证指数,2791.7645,2788.3141,2765.8066,2804.0932,2765.6394,0,0,253645375,228570135027,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2024-09-06,15:30:39,00,";
         * var hq_str_sz399001="深证成指,8252.006,8249.659,8130.770,8260.690,8128.767,0.000,0.000,36378777179,314062276972.566,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,2024-09-06,15:00:03,00";
         */

        //2.TODO:使用正则解析JS数据
        //定义正则表达式
        String regex="hq_str_(.+)=\"(.+)\";";
        //表达式编译
        Pattern pattern = Pattern.compile(regex);
        //匹配字符串
        Matcher matcher = pattern.matcher(resData);
        List<StockMarketIndexInfo>entities=new ArrayList<>();
        while (matcher.find()){//有多行数据(以 ; 结尾),要使用while循环
            //如果使用的是group(0),则获取匹配的全部数据
            //获取大盘的编码
            String marketCode = matcher.group(1);//获取第一个括号匹配的数据
            //获取大盘的其他信息
            String otherInfo=matcher.group(2);//获取第二个括号匹配的数据
            //分割其他信息成String数据
            String[] splitArr = otherInfo.split(",");
            //大盘名称
            String marketName=splitArr[0];
            //获取当前大盘的开盘点数
            BigDecimal openPoint=new BigDecimal(splitArr[1]);//直接把String类型的数据变成BigDecimal数据类型
            //前收盘点
            BigDecimal preClosePoint=new BigDecimal(splitArr[2]);
            //获取大盘的当前点数
            BigDecimal curPoint=new BigDecimal(splitArr[3]);
            //获取大盘最高点
            BigDecimal maxPoint=new BigDecimal(splitArr[4]);
            //获取大盘的最低点
            BigDecimal minPoint=new BigDecimal(splitArr[5]);
            //获取成交量
            Long tradeAmt=Long.valueOf(splitArr[8]);
            //获取成交金额
            BigDecimal tradeVol=new BigDecimal(splitArr[9]);
            //获取交易时间
            DateTime dateTime = DateTime.parse(splitArr[30] + " " + splitArr[31], DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
            Date date = dateTime.withSecondOfMinute(0).withMillisOfSecond(0).toDate();

            //TODO:3.把获取的数据封装成entity对象,用来插入数据库
            StockMarketIndexInfo entity = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId())//使用雪花算法生成一个Long类型的id
                    .marketCode(marketCode)
                    .marketName(marketName)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .curPoint(curPoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeAmount(tradeAmt)
                    .tradeVolume(tradeVol)
                    .curTime(date).build();
            entities.add(entity);
        }
        log.info("当前时间:{},获取的数据为:{}",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-ss HH:mm:ss")),entities);
        /**
         * 当前时间:2024-09-14 20:52:14,获取的数据为:
         * [
         * StockMarketIndexInfo(id=1832039060873678848, marketCode=sh000001, marketName=上证指数, preClosePoint=2788.3141, openPoint=2791.7645, curPoint=2765.8066, minPoint=2765.6394, maxPoint=2804.0932, tradeAmount=253645375, tradeVolume=228570135027, curTime=Fri Sep 06 15:30:00 CST 2024),
         * StockMarketIndexInfo(id=1832039103492001792, marketCode=sz399001, marketName=深证成指, preClosePoint=8249.659, openPoint=8252.006, curPoint=8130.770, minPoint=8128.767, maxPoint=8260.690, tradeAmount=36378777179, tradeVolume=314062276972.566, curTime=Fri Sep 06 15:00:00 CST 2024)
         * ]
         */
        //TODO:4.使用mybatis把entities集合批量入库
        int count=stockMarketIndexInfoMapper.insertBatch(entities);
        if(count>0){
            log.info("当前时间:{},{}数据插入成功",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),entities);
        }else{
            log.error("当前时间:{},{}数据插入失败",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),entities);
        }
    }
}

 mapper

    /**
     * 批量插入数据到数据库
     * @return
     */
    int insertBatch(@Param("entities") List<StockMarketIndexInfo>entities);

mapperXml文件

<insert id="insertBatch">
        insert into stock_market_index_info
        ( id,market_code,market_name
        ,pre_close_point,open_point,cur_point
        ,min_point,max_point,trade_amount
        ,trade_volume,cur_time)
        values
            <foreach collection="entities" item="entity" separator=",">
                (#{entity.id,jdbcType=BIGINT},#{entity.marketCode,jdbcType=CHAR},#{entity.marketName,jdbcType=VARCHAR}
                ,#{entity.preClosePoint,jdbcType=DECIMAL},#{entity.openPoint,jdbcType=DECIMAL},#{entity.curPoint,jdbcType=DECIMAL}
                ,#{entity.minPoint,jdbcType=DECIMAL},#{entity.maxPoint,jdbcType=DECIMAL},#{entity.tradeAmount,jdbcType=BIGINT}
                ,#{entity.tradeVolume,jdbcType=DECIMAL},#{entity.curTime,jdbcType=TIMESTAMP})
            </foreach>
    </insert>

第六步:查看数据库是否插入成功

 注意:在这个表中把cur_time 和market_code设置为联合唯一键,让同一时间内的国内大盘股票不可重复,即一个国内大盘股票在同一时间的数据只能有一个

把JS数据封装成entity类对象的功能抽取出来,成为一个工具类

public class MyParserStockInfoUtil {
    private IdWorker idWorker;//定义一个成员变量,使用雪花算法生成唯一id
    public MyParserStockInfoUtil(IdWorker idWorker){
        this.idWorker=idWorker;
    }

    /**
     * 用来解析JS数据格式,并把解析出的内容封装到entity类中,因为返回的JS数据大多数的多行的,所以返回值为List集合
     * @param stockStr JS数据
     * @param type 要封装到哪个entity类中
     *             1-->解析成A股大盘entity类
     *             2-->解析成国外大盘entity类
     *             3-->解析成A股entity类
     */
    public List parseStockMarketInfo(String stockStr, Integer type){
        List<Object>datas=new ArrayList<>();
        //判断传进来的JS数据是否为空
        if(StringUtils.isBlank(stockStr)){
            return datas;//直接返回空集合
        }
        //定义正则表达式
        String regex="hq_str_(.+)=\"(.+)\";";
        //编译正则表达式
        Pattern pattern = Pattern.compile(regex);
        //匹配JS数据
        Matcher matcher = pattern.matcher(stockStr);
        while (matcher.find()){//每次匹配一行数据,即每次循环解析成一个entity对象
            //解析成国内大盘数据
            if(type==ParseStockType.INNER){//当Integer包装类与int类型比较时,Integer类会自动解除包装,取出值来比较
                StockMarketIndexInfo info=parser4InnerStockMarket(matcher.group(1),matcher.group(2));
                datas.add(info);
            } else if (type==ParseStockType.OUTER) {//解析成外盘数据
                StockOuterMarketIndexInfo info=parser4OuterStockMarket(matcher.group(1),matcher.group(2));
                datas.add(info);
            } else if (type==ParseStockType.ASHARE) {//解析成A股
                StockRtInfo info=parser4StockRtInfo(matcher.group(1),matcher.group(2));
                datas.add(info);
            }else {
                return datas;//直接返回一个空集合
            }
        }
        return datas;
    }
    /**
     * 解析国外大盘数据
     * @param marketCode 大盘ID
     * @param otherInfo 大盘其它信息,以逗号间隔
     * @return
     */

    private StockOuterMarketIndexInfo parser4OuterStockMarket(String marketCode, String otherInfo) {
        //其他信息
        String[] others=otherInfo.split(",");
        //大盘名称
        String marketName = others[0];
        //大盘点数
        BigDecimal curPoint = new BigDecimal(others[1]);
        //涨跌值
        BigDecimal upDown = new BigDecimal(others[2]);
        //涨幅
        BigDecimal rose = new BigDecimal(others[3]);
        //获取当前时间
        Date date = DateTime.now().withSecondOfMinute(0).withMillisOfSecond(0).toDate();
        //组装实体对象,并直接返回
        return StockOuterMarketIndexInfo.builder()
                .id(idWorker.nextId())
                .marketCode(marketCode)
                .curPoint(curPoint)
                .updown(upDown)
                .rose(rose)
                .curTime(date)
                .build();
    }
    /**
     * 解析国内A股数据
     * @param stockCode 股票ID
     * @param otherInfo 股票其它信息,以逗号间隔
     * @return
     */
    private StockRtInfo parser4StockRtInfo(String stockCode, String otherInfo) {
        //去除股票编码前面sh,sz,存入数据库中不需要
        stockCode = stockCode.substring(2);
        //分割otherInfo信息
        String[] others = otherInfo.split(",");
        //大盘名称
        String stockName = others[0];
        //今日开盘价
        BigDecimal openPrice = new BigDecimal(others[1]);
        //昨日收盘价
        BigDecimal preClosePrice = new BigDecimal(others[2]);
        //当前价格
        BigDecimal currentPrice = new BigDecimal(others[3]);
        //今日最高价额
        BigDecimal maxPrice = new BigDecimal(others[4]);
        //今日最低价额
        BigDecimal minPrice = new BigDecimal(others[5]);
        //成交量
        Long tradeAmount = Long.valueOf(others[8]);
        //成金额
        BigDecimal tradeVol = new BigDecimal(others[9]);
       //时间
        DateTime dateTime = DateTime.parse(others[30] + " " + others[31], DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date date = dateTime.withSecondOfMinute(0).withMillisOfSecond(0).toDate();
        //封装成entity对象,并直接返回
        return  StockRtInfo.builder()
                .id(idWorker.nextId())
                .stockCode(stockCode)
                .stockName(stockName)
                .openPrice(openPrice)
                .preClosePrice(preClosePrice)
                .curPrice(currentPrice)
                .maxPrice(maxPrice)
                .minPrice(minPrice)
                .tradeAmount(tradeAmount)
                .tradeVolume(tradeVol)
                .curTime(date)
                .build();
    }
    /**
     * 解析国内大盘数据
     * @param marketCode 大盘ID
     * @param otherInfo 大盘其它信息,以逗号间隔
     * @return
     */

    private StockMarketIndexInfo parser4InnerStockMarket(String marketCode, String otherInfo) {
        //分割其他信息成String数据
        String[] splitArr = otherInfo.split(",");
        //大盘名称
        String marketName=splitArr[0];
        //获取当前大盘的开盘点数
        BigDecimal openPoint=new BigDecimal(splitArr[1]);//直接把String类型的数据变成BigDecimal数据类型
        //前收盘点
        BigDecimal preClosePoint=new BigDecimal(splitArr[2]);
        //获取大盘的当前点数
        BigDecimal curPoint=new BigDecimal(splitArr[3]);
        //获取大盘最高点
        BigDecimal maxPoint=new BigDecimal(splitArr[4]);
        //获取大盘的最低点
        BigDecimal minPoint=new BigDecimal(splitArr[5]);
        //获取成交量
        Long tradeAmt=Long.valueOf(splitArr[8]);
        //获取成交金额
        BigDecimal tradeVol=new BigDecimal(splitArr[9]);
        //获取交易时间
        DateTime dateTime = DateTime.parse(splitArr[30] + " " + splitArr[31], DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date date = dateTime.withSecondOfMinute(0).withMillisOfSecond(0).toDate();

        //封装成entity对象,并直接返回
        return StockMarketIndexInfo.builder()
                .id(idWorker.nextId())//使用雪花算法生成一个Long类型的id
                .marketCode(marketCode)
                .marketName(marketName)
                .openPoint(openPoint)
                .preClosePoint(preClosePoint)
                .curPoint(curPoint)
                .maxPoint(maxPoint)
                .minPoint(minPoint)
                .tradeAmount(tradeAmt)
                .tradeVolume(tradeVol)
                .curTime(date).build();
    }


}
public class ParseStockType {
    /**
     * A股大盘标识
     */
    public static final int INNER=1;

    /**
     * 国外大盘标识
     */
    public static final int OUTER=2;

    /**
     * A股标识
     */
    public static final int ASHARE=3;
}

在配置类中把工具类交给bean管理,并给成员变量进行依赖注入

@Configuration
//开启StockInfoConfig这个类,开始让这个类映射加载yml文件的属性,并交给spring管理
@EnableConfigurationProperties(StockInfoConfig.class)
public class CommonConfig {
    //配置雪花算法的工具类bean,交给spring管理
    @Bean
    public IdWorker idWorker(){
        /**
         * 参数一:机器ID
         * 参数二:机房ID
         */
        return new IdWorker(1L,2L);
    }

    /**
     * 解析JS数据成股票entity的工具类
     * @param idWorker 自动注入一个IdWorker Bean
     */
    @Bean
    public MyParserStockInfoUtil myParserStockInfoUtil(IdWorker idWorker){
        return new MyParserStockInfoUtil(idWorker);
    }
}

使用RestTemplate获取国内A股股票的基本信息,并存入数据库

这里我们使用了guava工具包,用于把集合进行分组

 <!--工具包-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>

数据库中个股的编码,我们向新浪网查询个股信息时,需要给股票编码加上前缀,6开头加"sh",0开头加"sz"

返回的个股JS数据

var hq_str_sh601006="大秦铁路, 27.55, 27.25, 26.91, 27.55, 26.20, 26.91, 26.92,22114263, 589824680, 4695, 26.91, 57590, 26.90, 14700, 26.89, 14300,
26.88, 15100, 26.87, 3100, 26.92, 8900, 26.93, 14230, 26.94, 25150, 26.95, 15220, 26.96, 2008-01-11, 15:05:32";
这个字符串由许多数据拼接在一起,不同含义的数据用逗号隔开了,按照程序员的思路,顺序号从0开始。
0:”大秦铁路”,股票名字;
1:”27.55″,今日开盘价;
2:”27.25″,昨日收盘价;
3:”26.91″,当前价格;
4:”27.55″,今日最高价;
5:”26.20″,今日最低价;
6:”26.91″,竞买价,即“买一”报价;
7:”26.92″,竞卖价,即“卖一”报价;
8:”22114263″,成交的股票数,由于股票交易以一百股为基本单位,所以在使用时,通常把该值除以一百;
9:”589824680″,成交金额,单位为“元”,为了一目了然,通常以“万元”为成交金额的单位,所以通常把该值除以一万;
10:”4695″,“买一”申请4695股,即47手;
11:”26.91″,“买一”报价;
12:”57590″,“买二”
13:”26.90″,“买二”
14:”14700″,“买三”
15:”26.89″,“买三”
16:”14300″,“买四”
17:”26.88″,“买四”
18:”15100″,“买五”
19:”26.87″,“买五”
20:”3100″,“卖一”申报3100股,即31手;
21:”26.92″,“卖一”报价
(22, 23), (24, 25), (26,27), (28, 29)分别为“卖二”至“卖四的情况”
30:”2008-01-11″,日期;
31:”15:05:32″,时间;

查询思路:我们把查询到的A股个股股票编码进行分组,15个股票编码一组,使用foreach方法进行循环

我们使用restTemplate查询时会查询出15行JS数据,然后把这个15行JS数据传入工具类进行正则表达式匹配,一共会匹配15次,并封装到15个个股entity类对象中,然后我们就会接收到List<StockRtInfo>集合,最后我们只需要把这个集合插入数据库即可

查询所有个股股票编码实现

    /**
     * 获取所有A股的编码
     */
    List<String> getAllStockCode();
 <select id="getAllStockCode" resultType="string">
        select stock_code  from stock_business
    </select>

总体业务实现 

@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Autowired
    private StockBusinessMapper stockBusinessMapper;
    @Autowired
    private MyParserStockInfoUtil myParserStockInfoUtil;
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;
    /**
     * 直接把请求对象抽取出来,因为封装的请求头的格式是固定的
     */
    private  HttpEntity<Object> httpEntity;
    /**
     * 采集A股的信息,并批量入库
     */
    @Override
    public void getStockRtInfo() {
        //获取所有的A股编码信息
        List<String>allCodes=stockBusinessMapper.getAllStockCode();
        //http://hq.sinajs.cn/list=sh601003,sh601001,sz000019
        //TODO:给取出的编码加上前缀,6开头加sh,0开头加sz
        allCodes = allCodes.stream().map(code -> code.startsWith("6") ? "sh" + code : "sz" + code).collect(Collectors.toList());

        //把编码信息分组进行查询,不然请求url过长,会出现错误
        //使用google的guava包
        Lists.partition(allCodes,15).forEach(codes->{//15个股票编码为一组
            //TODO:使用RestTemplate发送请求,获取个股的JS格式信息
            //设置url
            String url=stockInfoConfig.getMarketUrl()+String.join(",",codes);
           /* //设置请求头
            HttpHeaders headers = new HttpHeaders();
            //必须填写,否则数据采集不到
            headers.add("Referer","https://finance.sina.com.cn/stock/");
            headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
            //组装请求对象
            HttpEntity<Object> httpEntity = new HttpEntity<>(headers);*/

            //使用restTemplate发送请求数据
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
            //获取响应码
            int statusCode= responseEntity.getStatusCodeValue();
            if(statusCode!=200){
                log.error("时间:{},采集数据出错,响应码状态为:{}", DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),statusCode);
                return;
            }
            //获取js响应数据
            String resData = responseEntity.getBody();
//会查询出15行个股JS数据
            log.info("时间:{},采集数据成功,数据为:{}",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")),resData);
            //TODO:使用工具类把JS数据封装成个股entity对象
            List<StockRtInfo> list = myParserStockInfoUtil.parseStockMarketInfo(resData, ParseStockType.ASHARE);

            //TODO:批量把15个 个股entity对象入库
            int count=stockRtInfoMapper.insertBatch(list);
            if(count>0){
                log.info("插入成功");
            }else {
                log.error("插入失败");
            }
        });
    @PostConstruct//在初始化所有bean后 自动调用改方法
    private void initData(){
        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        //必须填写,否则数据采集不到
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        //组装请求对象
       httpEntity = new HttpEntity<>(headers);
    }
    }

 mapper

    /**
     * 把StockRtInfo对象批量入库
     * @param list StockRtInfo对象集合
     */
    int insertBatch(@Param("list") List<StockRtInfo> list);

mapperXml

<insert id="insertBatch">
        insert into stock_rt_info
        ( id,stock_code,stock_name
        ,pre_close_price,open_price,cur_price
        ,min_price,max_price,trade_amount
        ,trade_volume,cur_time)
        values
            <foreach collection="list" item="info" separator=",">
                (#{info.id,jdbcType=BIGINT},#{info.stockCode,jdbcType=CHAR},#{info.stockName,jdbcType=VARCHAR}
                ,#{info.preClosePrice,jdbcType=DECIMAL},#{info.openPrice,jdbcType=DECIMAL},#{info.curPrice,jdbcType=DECIMAL}
                ,#{info.minPrice,jdbcType=DECIMAL},#{info.maxPrice,jdbcType=DECIMAL},#{info.tradeAmount,jdbcType=BIGINT}
                ,#{info.tradeVolume,jdbcType=DECIMAL},#{info.curTime,jdbcType=TIMESTAMP})
            </foreach>

    </insert>

结果:

使用RestTemplate获取国内板块的信息,并存入数据库

 返回的JS数据

var S_Finance_bankuai_sinaindustry = {
"new_blhy":"new_blhy,玻璃行业,19,19.293684210526,-0.17052631578947,-0.87610188740468,315756250,5258253314,sh600586,3.464,9.260,0.310,金晶科技",
"new_cbzz":"new_cbzz,船舶制造,8,12.15875,0.0125,0.10291242152928,214866817,2282104956,sh600150,0.978,24.790,0.240,中国船舶",
  //........省略.......
}

解析思路:

分析发现,板块的数据格式与大盘、个股数据格式不一致,但是都是js格式,我们通过RestTemplate拉取数据后,进行解析处理:

  • 直接使用=正则切割,就可获取一个标准的json格式数据;
  • 调用json的工具类(gson等)将json的数据转化成Map对象;
  • 获取map中的value值,逐个切割解析,然后封装成板块数据,然后批量插入到数据库下;

j解析成map的value值: 

数据格式:
"new_blhy,玻璃行业,19,19.293684210526,-0.17052631578947,-0.87610188740468,315756250,5258253314,sh600586,3.464,9.260,0.310,金晶科技"
参数语义:
['0.板块编码',1.'板块名称',2.'公司家数',3.'平均价格',4.'涨跌额',5.'涨跌幅',6.'总成交量',7.'总成交金额',8.'领涨股代码',9.'涨跌幅',10.'当前价',11.'涨跌额',12.'领涨股名称']

 gson导入

<!--json工具包-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>

示例

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Map;

public class GsonExample {
    public static void main(String[] args) {
        String json = "{\"name\":\"Kimi\",\"age\":25}";
        Gson gson = new Gson();
        Type type = new TypeToken<Map<String, Object>>(){}.getType();
        Map<String, Object> map = gson.fromJson(json, type);
        System.out.println(map);
    }
}

把JS格式数据转换成List<entity>集合工具类编写

/**
     * 用来解析板块JS数据,并把解析后的数据封装到entity类中
     * @param blockStr JS数据
     * @return
     */
/*
JS数据如下:
var S_Finance_bankuai_sinaindustry = {
"new_blhy":"new_blhy,玻璃行业,19,19.293684210526,-0.17052631578947,-0.87610188740468,315756250,5258253314,sh600586,3.464,9.260,0.310,金晶科技",
"new_cbzz":"new_cbzz,船舶制造,8,12.15875,0.0125,0.10291242152928,214866817,2282104956,sh600150,0.978,24.790,0.240,中国船舶",
  //........省略.......
}
 */
    public List parseStockBlockInfo(String blockStr){
        List<Object>datas=new ArrayList<>();
        if(StringUtils.isBlank(blockStr)){
            return datas;//直接返回一个空集合
        }
        //TODO:1.使用=作为分割符把JS数据把json数据分割出来
         String jsonData=blockStr.split("=")[1];
        //TODO:2.把json数据转换成Map集合
        Gson gson = new Gson();
        Type type = new TypeToken<HashMap<String, String>>(){}.getType();
        HashMap<String, String> dataMap = gson.fromJson(jsonData, type);
        dataMap.forEach((key,value)->{
            //TODO:3.使用value值,进行分割
            String[] split = value.split(",");

            //TODO:4.封装成entity类
            StockBlockRtInfo info = StockBlockRtInfo.builder()
                    .id(idWorker.nextId())
                    .label(split[0])
                    .blockName(split[1])
                    .companyNum(new Integer(split[2]))
                    .avgPrice(new BigDecimal(split[3]))
                    .updownRate(new BigDecimal(split[5]))
                    .tradeAmount(new Long(split[6]))
                    .tradeVolume(new BigDecimal(split[7]))
                    .curTime(DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate())//获取当前时间的最新交易点
                    .build();
            datas.add(info);
                }
                );
        return datas;
    }

业务实现

    /**
     * 采集国内板块信息,并批量入库
     */
    @Override
    public void getStockBlockInfo() {
        //设置url
        String url = stockInfoConfig.getBlockUrl();
        //发送请求
        //这个的请求变量已经封装了请求头参数
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //获取响应的JS数据
        String resData = responseEntity.getBody();
        //调用工具类获取解析JS数据后的entity对象集合
        List<StockBlockRtInfo> list = myParserStockInfoUtil.parseStockBlockInfo(resData);
        //System.out.println(list);

        //批量插入数据
        int count=stockBlockRtInfoMapper.insertBatch(list);
        if(count>0){
            log.info("插入成功,数量为:{}",count);
        }else {
            log.error("当前时间:{},插入失败",DateTime.now().toString(DateTimeFormat.forPattern("yyyy-HH-mm HH:mm:ss")));
        }
    }

mapper

 /**
     * 批量插入板块数据
     * @param list 板块entity对象集合
     */
    int insertBatch(@Param("list") List<StockBlockRtInfo> list);

mapperXml

    <insert id="insertBatch">
        insert into stock_block_rt_info
        ( id,label,block_name
        ,company_num,avg_price,updown_rate
        ,trade_amount,trade_volume,cur_time
        )
        values
            <foreach collection="list" item="info" separator=",">
                (#{info.id,jdbcType=BIGINT},#{info.label,jdbcType=VARCHAR},#{info.blockName,jdbcType=VARCHAR}
                ,#{info.companyNum,jdbcType=INTEGER},#{info.avgPrice,jdbcType=DECIMAL},#{info.updownRate,jdbcType=DECIMAL}
                ,#{info.tradeAmount,jdbcType=BIGINT},#{info.tradeVolume,jdbcType=DECIMAL},#{info.curTime,jdbcType=TIMESTAMP}
                )
            </foreach>

    </insert>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落落落sss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值