【java爬虫】基于springboot+jdbcTemplate+sqlite+OkHttp获取个股的详细数据

注:本文所用技术栈为:springboot+jdbcTemplate+sqlite+OkHttp

前面的文章我们获取过沪深300指数的成分股所属行业以及权重数据,本文我们来获取个股的详细数据。

我们的数据源是某狐财经,接口的详细信息在下面的文章中,本文就不再赘述了

用爬虫分析沪深300指数超长走势-CSDN博客

下面是一组url和返回值的示例

https://q.stock.sohu.com/hisHq?code=cn_000001&start=20190101&end=20190102&stat=1&order=D&period=d&callback=historySearchHandler&rt=jsonp
historySearchHandler([{"status":0,"hq":[["2019-01-02","9.39","9.19","-0.19","-2.03%","9.16","9.42","539386","49869.51","0.31%"]],"code":"cn_000001","stat":["累计:","2019-01-02至2019-01-02","-0.19","-2.03%",9.16,9.42,539386,49869.51,"0.31%"]}])

我们需要关心的是"hq"中的值,"hq"中的值是一个列表,列表中还有很多列表,每个列表代码一组数据,至于数据的具体含义,可以登陆搜狐财经网站上去看看。

宁德时代(300750) - 历史行情 - 股票行情中心 - 搜狐证券 (sohu.com)

这边我就随便截取一端数据

数据的从左到右分别代表日期,开盘价,收盘价,涨跌额,涨跌幅,最低,最高,成交量,成交金额和换手率,最后的盘后量是没有的。

那么我们就可以根据上述信息建立数据表和实体类

    @Override
    public void createTbaleIfNotExist() {
        Integer count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name = ?", Integer.class, TABLE_NAME);
        if (count == 0) {
            String sql = "CREATE TABLE " + TABLE_NAME + "(" +
                    "id VARCHAR(50) PRIMARY KEY," +
                    "code VARCHAR(20)," +           // 股票代码
                    "record_date VARCHAR(20)," +    // 记录的时间
                    "open_price float," +           // 开盘价
                    "close_price float," +           // 收盘价
                    "change_ament float," +          // 涨跌额
                    "change_range float," +          // 涨跌幅
                    "max_price float," +             // 最高价格
                    "min_price float," +             // 最低价格
                    "volume float," +                // 成交量(手)
                    "turnover float," +              // 成交额(万)
                    "turnover_rate float)";               // 换手率
            jdbcTemplate.execute(sql);
            log.info(TABLE_NAME + "建表成功");
        } else {
            log.info("建表失败,表格已存在");
        }
    }
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StockEntity {
    private String id;
    private String code;
    private String record_date;
    private Double open_price;
    private Double close_price;
    private Double change_amend;
    private Double change_range;
    private Double max_price;
    private Double min_price;
    private Double volume;
    private Double turnover;
    private Double turnover_rate;

    // 将数据转换为Object数组
    public Object[] changeToArray() {
        Object[] arr = new Object[]{
                id,
                code,
                record_date,
                open_price.toString(),
                close_price.toString(),
                change_amend.toString(),
                change_range.toString(),
                max_price.toString(),
                min_price.toString(),
                volume.toString(),
                turnover.toString(),
                turnover_rate.toString()
        };
        return arr;
    }

}

其中id字段是用来放置重复插入的,他的值是code+日期,这样就能保证某一只股票当日的数据是唯一的。

下面是最重要的获取数据和插入数据的方法。

我们采用批量插入的方法,传入一个列表,一次性将列表中所有的值都插入数据库

    @Override
    public void insertItems(List<StockEntity> entityList) {
        String sql = "INSERT OR IGNORE INTO " + TABLE_NAME + " (id, code, record_date," +
                "open_price, close_price, change_ament," +
                "change_range, max_price, min_price," +
                "volume, turnover, turnover_rate) values (?,?,?,?,?,?,?,?,?,?,?,?)";
        // 将列表转为Object数组
        List<Object[]> arr = new ArrayList<>();
        for(int i=0; i<entityList.size(); i++) {
            arr.add(entityList.get(i).changeToArray());
        }
        jdbcTemplate.batchUpdate(sql, arr);
    }

下面就是获取数据的代码

    // 获取数据并且存入数据库
    // 三个参数分别是:股票代码,开始时间和结束时间
    // 开始时间和结束时间都填年份,代码中会自动补全具体时间
    public int getDataByYear(String code, String start, String end) {
        String url = "https://q.stock.sohu.com/hisHq?";
        Request request = null;
        Response response = null;
        int num = 0;
        try {
            for (int i = Integer.parseInt(start); i <= Integer.parseInt(end); i++) {
                for (int j = 1; j <= 12; j++) {
                    HttpUrl.Builder httpBuiler = HttpUrl.parse(url).newBuilder();
                    String starttime = null;
                    String endtime = null;
                    if (j != 12) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(i);
                        if (j < 10) {
                            sb.append("0");
                        }
                        sb.append(j);
                        sb.append("01");
                        starttime = sb.toString();
                        sb = new StringBuilder();
                        sb.append(i);
                        if (j + 1 < 10) {
                            sb.append("0");
                        }
                        int tmp = j + 1;
                        sb.append(tmp);
                        sb.append("01");
                        endtime = sb.toString();
                    } else {
                        starttime = i + "1201";
                        endtime = i + "1231";
                    }
                    log.info("开始计算时间段[" + starttime + "," + endtime + "]内数据");
                    httpBuiler.addQueryParameter("code", "cn_" + code);
                    httpBuiler.addQueryParameter("start", starttime);
                    httpBuiler.addQueryParameter("end", endtime);
                    httpBuiler.addQueryParameter("stat", "1");
                    httpBuiler.addQueryParameter("order", "D");
                    httpBuiler.addQueryParameter("period", "d");
                    httpBuiler.addQueryParameter("callback", "history");
                    httpBuiler.addQueryParameter("rt", "jsonp");
                    request = new Request.Builder()
                            .url(httpBuiler.build())
                            .get()   //默认就是GET请求,可以不写
                            .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36")
                            .build();

                    response = client.newCall(request).execute();
                    String res = response.body().string();
                    log.info("请求得到的数据:" + res);
                    // 将数据解析成List列表
                    if (!res.equals(NO_DATA_RESPONSE1) && !res.equals(NO_DATA_RESPONSE2)) {
                        List<StockEntity> entities = parseStrToArr(res, code);
                        sqLiteStockDao.insertItems(entities);
                        log.info("时间段[" + starttime + "," + endtime + "]内有" + entities.size() + "条数据");
                        num += entities.size();
                    } else {
                        log.info("时间段[" + starttime + "," + endtime + "]没有数据");
                    }

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return num;
    }

    // 将string数据解析成List列表
    private List<StockEntity> parseStrToArr(String res, String code) {
        List<StockEntity> entities = new ArrayList<>();
        res = res.split("\\(\\[")[1].split("]\\)")[0];
        JSONObject jsonObject = JSON.parseObject(res);
        // 获取 hq 字段的值
        Object hq = jsonObject.get("hq");
        // 判断 hq 的值是否为数组
        if (hq instanceof JSONArray) {
            // 遍历数组
            for (Object arr : (JSONArray) hq) {
                JSONArray jsonArray = (JSONArray) arr;
                StockEntity entity = new StockEntity();
                entity.setRecord_date((String) jsonArray.get(0));
                Double open_price = Double.parseDouble((String) jsonArray.get(1));
                Double close_price = Double.parseDouble((String) jsonArray.get(2));
                Double change_amend = Double.parseDouble((String) jsonArray.get(3));
                Double change_range = Double.parseDouble(((String) jsonArray.get(4)).split("%")[0]);
                Double max_price = Double.parseDouble((String) jsonArray.get(5));
                Double min_price = Double.parseDouble((String) jsonArray.get(6));
                Double volume = Double.parseDouble((String) jsonArray.get(7));
                Double turnover = Double.parseDouble((String) jsonArray.get(8));
                Double turnover_rate = Double.parseDouble(((String) jsonArray.get(9)).split("%")[0]);
                entity.setOpen_price(open_price);
                entity.setClose_price(close_price);
                entity.setChange_amend(change_amend);
                entity.setChange_range(change_range);
                entity.setMax_price(max_price);
                entity.setMin_price(min_price);
                entity.setVolume(volume);
                entity.setTurnover(turnover);
                entity.setTurnover_rate(turnover_rate);
                entity.setCode(code);
                entity.setId(entity.getCode() + "_" + (String) jsonArray.get(0));
                entities.add(entity);
            }
        }
        return entities;
    }

主要就是获取了数据然后进行解析,每一次解析都是从当前月份的1日到第二个月的1日,如果是12月的话是从12月1日到12月31日。

最后提供一个get接口进行方法的调用

    @RequestMapping("/getDataByYear/{code}/{start}/{end}")
    @ResponseBody
    public String getDataByYear(@PathVariable("code") String code,
                                @PathVariable("start") String start,
                                @PathVariable("end") String end) {
        Integer num = stockService.getDataByYear(code, start, end);
        return num.toString();
    }

最后获取到的数据是这样的

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值