【java爬虫】获取个股详细数据并用echarts展示

news2025/4/24 19:41:27

前言

前面一篇文章介绍了获取个股数据的方法,本文将会对获取的接口进行一些优化,并且添加查询数据的接口,并且基于后端返回数据编写一个前端页面对数据进行展示。

具体的获取个股数据的接口可以看上一篇文章

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

下面是操作演示,首先是爬虫获取股票数据

接着是进行获取个股详细数据并且进行数据展示

数据图表还可以下载下来,下面是下载下来的图片,不过下载下来的图片就不能查看每个点的详细数据了

后端接口

相对于前文,后端接口进行了一定优化,每年的数据分3次获取,时间段分别是0101-0501,0501-0901和0901-1231,并且每次请求都是从2023年开始逐年往前获取,一旦发现没有数据了就停止获取。

服务类的详细代码如下

@Slf4j
@Service
public class StockService {

    // 没有数据对应的返回
    private final String NO_DATA_RESPONSE1 = "historySearchHandler({})";
    private final String NO_DATA_RESPONSE2 = "history({})";

    @Autowired
    private SQLiteStockDao sqLiteStockDao;

    // 获取一个OKHttp实例
    private OkHttpClient client = new OkHttpClient()
            .newBuilder()
            .connectTimeout(1000, TimeUnit.SECONDS)
            .build();

    public void clearAll() {
        sqLiteStockDao.clearAll();
    }

    public void createTbaleIfNotExist() {
        sqLiteStockDao.createTbaleIfNotExist();
    }

    // 查询所有的数据
    public List<StockEntity> queryAllByCode(String code) {
        return sqLiteStockDao.queryAllByCode(code);
    }


    // 获取数据并且存入数据库
    // 三个参数分别是:股票代码,开始时间和结束时间
    // 开始时间和结束时间都填年份,代码中会自动补全具体时间
    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;
        // 一年的数据分三次请求
        String[] startTime = {"0101", "0501", "0901"};
        String[] endTime = {"0501", "0901", "1231"};
        try {
            for (int i = Integer.parseInt(end); i >= Integer.parseInt(start); i--) {
                for (int j = startTime.length-1; j >=0; j--) {
                    HttpUrl.Builder httpBuiler = HttpUrl.parse(url).newBuilder();
                    String starttime = i + startTime[j];
                    String endtime = i + endTime[j];
                    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);
                    if (res.contains(NO_DATA_RESPONSE1) || res.contains(NO_DATA_RESPONSE2)) {
                        // 如果返回为空就认为后面没有数据了
                        log.info("时间段[" + starttime + "," + endtime + "]没有数据");
                        return num;
                    } else {
                        List<StockEntity> entities = parseStrToArr(res, code);
                        sqLiteStockDao.insertItems(entities);
                        log.info("时间段[" + starttime + "," + endtime + "]内有" + entities.size() + "条数据");
                        num += entities.size();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return num;
    }

    // 将string数据解析成List列表
    private List<StockEntity> parseStrToArr(String res, String code) {
        if (res.contains(NO_DATA_RESPONSE1) || res.contains(NO_DATA_RESPONSE2)) {
            return new ArrayList<>();
        }
        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;
    }

}

Dao层新增了查询某一只股票详细数据的方法,详细代码如下

@Slf4j
@Repository
public class SQLiteStockDao implements StockDao {

    private final String TABLE_NAME = "stock_table";

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void clearAll() {
        String sql = "DELETE FROM " + TABLE_NAME;
        jdbcTemplate.batchUpdate(sql);
        log.info("成功清空数据表" + TABLE_NAME);
    }

    @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("建表失败,表格已存在");
        }
    }

    @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);
    }

    @Override
    public List<StockEntity> queryAllByCode(String code) {
        String sql = "SELECT open_price, close_price, record_date, volume FROM " + TABLE_NAME +" WHERE code=? ORDER BY record_date DESC";
        log.info("执行sql:" + sql);
        List<StockEntity> stockEntities = jdbcTemplate.query(sql, new Object[]{code}, new BeanPropertyRowMapper<>(StockEntity.class));
        return stockEntities;
    }


}

Dao层对应的实体类如下

@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;
    }

}

最后就是提供给前端调用的接口了,主要是获取某一只股票的数据,只需要传入股票代码就能开始获取数据,还有查询的接口,同样是输入股票代码进行查询,控制类的详细代码如下

@Controller
@CrossOrigin
@RequestMapping("/stock")
public class StockController {

    private final String START_YEAR = "1985";
    private final String END_YEAR = "2023";

    @Autowired
    private StockService stockService;

    @RequestMapping("/clear")
    @ResponseBody
    public String clear() {
        stockService.clearAll();
        return "success";
    }

    @RequestMapping("/createTable")
    @ResponseBody
    public String getData() {
        stockService.createTbaleIfNotExist();
        return "success";
    }

    @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();
    }

    @RequestMapping("/getData/{code}")
    @ResponseBody
    public String getData(@PathVariable("code") String code) {
        Integer num = stockService.getDataByYear(code, START_YEAR, END_YEAR);
        List<StockEntity> stockEntityList = stockService.queryAllByCode(code);
        return JSON.toJSONString(stockEntityList);
    }

    @RequestMapping("/queryData/{code}")
    @ResponseBody
    public String queryData(@PathVariable("code") String code) {
        List<StockEntity> stockEntityList = stockService.queryAllByCode(code);
        return JSON.toJSONString(stockEntityList);
    }
}

前端页面

下面来说一下前端页面的编写,前端页面一共分为三个大块,

  • 沪深300成分股数据和操作按钮,通过按钮可以进行数据获取或者数据展示
  • 个股详细数据,这一个表格的内容会在你选定具体的股票后变更
  • 数据展示,选定个股后会动态生成展示的数据

前端主要用了vue+element-plus+axios+echarts进行编写,echarts表格参数参考了官方示例,由于数据量比较大,所以选用了大数据量的图表,参考的地址如下

Examples - Apache ECharts

下面展示页面的详细代码

<template>
  <div>
    <el-row class="container">
      <div class="left-grid">
        <el-card class="box-card">
          <template #header>
            <div class="card-header">
              <span>沪深300成分股</span>
            </div>
          </template>
          <el-table
            :data="table_data"
            :show-header="true"
            :max-height="250"
            stripe
          >
            <el-table-column
              type="index"
              label="序号"
              width="65%"
            ></el-table-column>
            <el-table-column
              prop="code"
              label="股票代码"
              width="85%"
            ></el-table-column>
            <el-table-column
              prop="name"
              label="公司简称"
              width="85%"
            ></el-table-column>
            <el-table-column prop="industry" label="操作">
              <template #default="scope">
                <el-button
                  type="primary"
                  size="small"
                  @click="queryData(scope.row)"
                  >查询</el-button
                >
                <el-button
                  type="primary"
                  size="small"
                  @click="getData(scope.row)"
                  >获取</el-button
                >
              </template>
            </el-table-column>
          </el-table>
        </el-card>
        <el-card>
          <template #header>
            <div class="card-header">
              <span>{{ table_title }}</span>
            </div>
          </template>
          <el-table
            v-loading="loading"
            :data="stock_data"
            :show-header="true"
            :max-height="220"
            stripe
          >
            <el-table-column prop="record_date" label="时间"></el-table-column>
            <el-table-column prop="open_price" label="开盘价"></el-table-column>
            <el-table-column
              prop="close_price"
              label="收盘价"
            ></el-table-column>
            <el-table-column
              prop="volume"
              label="成交量(手)"
            ></el-table-column>
          </el-table>
        </el-card>
      </div>

      <div class="right-grid" ref="myChart"></div>
    </el-row>
  </div>
</template>

<script>
import axios from "axios";
import { ElMessage } from "element-plus";
import { getCurrentInstance } from "vue";
export default {
  data() {
    return {
      update_status: "未开始",
      loading: true,
      table_title: "个股数据",
      // 沪深300成分股数据
      table_data: [],
      // 个股详细数据
      stock_data: [],
      echarts: getCurrentInstance().appContext.config.globalProperties.$echarts,
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      var url = "http://localhost:9001/queryAll";
      axios
        .get(url)
        .then((response) => {
          this.table_data = response.data;
          console.log(response);
          this.loading = false;
        })
        .catch((error) => {
          console.log(error);
          this.loading = false;
        });
    },
    // 绘制折线图
    create_axis() {
      //3.初始化实例对象 echarts.init(dom容器)
      var data_xAxis = [];
      var data_yAxis = [];
      for (var i = this.stock_data.length - 1; i >= 0; i--) {
        data_xAxis.push(this.stock_data[i].record_date);
        data_yAxis.push(this.stock_data[i].close_price);
      }
      console.log(data_xAxis);
      console.log(data_yAxis);
      var dom = this.$refs["myChart"]; // 获取dom节点
      var myChart = this.echarts.init(dom);
      //4.指定配置项和数据
      var option = {
        tooltip: {
          trigger: "axis",
          position: function (pt) {
            return [pt[0], "10%"];
          },
        },
        title: {
          left: "center",
          text: this.table_title,
        },
        toolbox: {
          feature: {
            dataZoom: {
              yAxisIndex: "none",
            },
            restore: {},
            saveAsImage: {},
          },
        },
        xAxis: {
          type: "category",
          boundaryGap: false,
          data: data_xAxis,
        },
        yAxis: {
          type: "value",
          boundaryGap: [0, "100%"],
        },
        dataZoom: [
          {
            type: "inside",
            start: 0,
            end: 10,
          },
          {
            start: 0,
            end: 10,
          },
        ],
        series: [
          {
            name: this.table_title,
            type: "line",
            symbol: "none",
            sampling: "lttb",
            itemStyle: {
              color: "rgb(135,206,235)",
            },
            areaStyle: {
              color: new this.echarts.graphic.LinearGradient(0, 0, 0, 1, [
                {
                  offset: 0,
                  color: "rgb(135,206,250)",
                },
                {
                  offset: 1,
                  color: "rgb(135,206,235)",
                },
              ]),
            },
            data: data_yAxis,
          },
        ],
      };
      //5.将配置项设置给echarts实例对象,使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
    },
    // 查询数据
    queryData(row) {
      var url = "http://localhost:9001/stock/queryData/" + row.code;
      this.loading = true;
      this.table_title = row.code + " " + row.name;
      ElMessage("开始查询 " + this.table_title + " 的数据");
      axios
        .get(url)
        .then((response) => {
          this.stock_data = response.data;
          console.log(response);
          this.loading = false;
          ElMessage({
            message: "查询 " + this.table_title + " 的数据成功",
            type: "success",
          });
          // 绘制数据
          this.create_axis();
        })
        .catch((error) => {
          console.log(error);
          this.loading = false;
          ElMessage.error("查询 " + this.table_title + " 的数据失败");
        });
    },
    // 获取数据
    getData(row) {
      var url = "http://localhost:9001/stock/getData/" + row.code;
      this.loading = true;
      this.table_title = row.code + " " + row.name;
      ElMessage("开始获取 " + this.table_title + " 的数据");
      axios
        .get(url)
        .then((response) => {
          this.stock_data = response.data;
          console.log(response);
          this.loading = false;
          ElMessage({
            message: "获取 " + this.table_title + " 的数据成功",
            type: "success",
          });
          // 绘制数据
          this.create_axis();
        })
        .catch((error) => {
          console.log(error);
          this.loading = false;
          ElMessage.error("获取 " + this.table_title + " 的数据失败");
        });
    },
  },
};
</script>

<style scoped>
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.container {
  display: grid;
  grid-template-columns: 35% 65%;
  width: 100%;
  height: 80vh;
}
.left-grid {
  background-color: #f0f0f0;
  border-radius: 2%;
  padding: 10px;
  height: 95%;
}
.right-grid {
  background-color: #f9ecc3;
  border-radius: 2%;
  padding: 10px;
  height: 95%;
}
</style>

前端页面有一个问题,就是数据量非常非常大,页面会很卡,这个问题的其中一个解决办法就是在获取数据的时候颗粒度可以小一点,比如一个星期获取一个数据之类的,因为一张图表也不可能展示出所有的数据,大家可能也只是想看一个总体的走势图,不过本文没有进行相关的优化,因为个人自用的话这点卡顿是可以接受的。 

结语

本文展示了通过网络爬虫获取个股详细数据,并且进行数据展示的方法,通过这个方法可以查询个股数据,并且用图表的方式将股票价格展示出来,这样可以非常直观地观察某一只股票的价格走势,由于获取到的数据量非常大,后期还可以进行一定的数据分析,如果你有什么想法欢迎和我交流,下面展示一下获取到的股票走势图。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1345516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目记录:利用Redis实现缓存以提升查询效率

一、概述 当我们查询所有数据时&#xff0c;如果缓存中没有&#xff0c;则去数据库查询&#xff0c;如果有&#xff0c;直接查缓存的数据就行。注意定期更新缓存数据。 二、主体代码 private static final String ROOM_SCHEDULES_HASH "RoomSchedules";Overridepu…

虚拟化技术和云计算的关系

1、云计算底层就是虚拟化技术。 &#xff08;1&#xff09;常见的虚拟化技术&#xff1a;VMware&#xff08;闭源的&#xff0c;需要收费&#xff09;、XEN、KVM &#xff08;2&#xff09;大部分公司用的虚拟化方案&#xff1a;XEN、KVM 2、虚拟化的历史 &#xff08;1&am…

安全配置审计概念、应用场景、常用基线及扫描工具

软件安装完成后都会有默认的配置&#xff0c;但默认配置仅保证了服务正常运行&#xff0c;却很少考虑到安全防护问题&#xff0c;攻击者往往利用这些默认配置产生的脆弱点发起攻击。虽然安全人员已经意识到正确配置软件的重要性&#xff0c;但面对复杂的业务系统和网络结构、网…

QT上位机开发(乘法计算小软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面一篇文章&#xff0c;我们学习了怎么创建qt的第一个工程&#xff0c;怎么用designer给qt修改界面。虽然我们到目前为止&#xff0c;还没有编写…

苹果CMS超级播放器专业版无授权全开源,附带安装教程

源码介绍 超级播放器专业版v1.0.8&#xff0c;内置六大主流播放器&#xff0c;支持各种格式的视频播放&#xff0c;支持主要功能在每一个播放器内核中都相同效果。 搭建教程 1.不兼容IE浏览器 2.php版本推荐7.4 支持7.1~7.4 3.框架引入不支持同时引入多个播放器 json对接教…

java基础之String的不可变性

目录 概述 String是如何实现不可变的 String为何设计成不可变的 1.缓存和性能优化 2.安全性 3.线程安全性 4.API设计和预测性能 概述 String类的不可变性意味着一旦创建了一个字符串对象&#xff0c;它的值就不能被修改。 String是如何实现不可变的 查看源码 public …

sklearn学习的一个例子用pycharm jupyter

环境 运行在jupyter 进行开发。即一个WEB端的开发工具。能适时显示开发的输出。后缀用的是ipynb.pycharm也可以支持。但也要提示按装jupyter. 或直接用andcoda 这里我们用pycharm进行项目创建 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jupyterlab pip ins…

1、gdb基本功能

文章目录 1、gdb1.1、运行1.1.1、程序入参 1.2、断点及观察点1.2.1、设置断点1.2.2、禁用、删除断点1.2.3、观察点 1.3、打印1.3.1、设定打印参数1.3.2、打印数据1.3.3、自动打印1.3.4、按照地址打印 linux下我现在接触到的常用调试工具如下. gbdgdbguicmake-tools gdb是最为通…

win11出现安全中心空白和IT管理员已限制对某些区域的访问(不一样的解决方式),真实的个人经历,并且解决经过

1、个人的产生问题的经历 2023年12月22日&#xff0c;由于我买了一块电脑的固态硬盘1T&#xff0c;想要扩容&#xff0c;原来电脑自带512G(由于个人是一个程序员&#xff0c;导致512G实在太古鸡肋)装好以后&#xff0c;想要重装一下系统&#xff0c;来个大清理。结果不出意料&…

小程序域名SSL证书能否用免费的?

在小程序开发中&#xff0c;确保通信安全性是至关重要的一环&#xff0c;而SSL证书正是为此提供了有效的保障。SSL证书通过加密数据传输&#xff0c;防止数据被中间人恶意窃取或篡改&#xff0c;为用户和应用提供了更安全的通信环境。 针对小程序域名的SSL证书&#xff0c;通常…

SpringBoot用JDK1.8的依赖设置pom.xml

pom.xml的修改主要是两个地方&#xff1a; 1.修改springframework的版本为2.5.0&#xff0c;版本太高可能和其他插件搭配有冲突&#xff1b; 2.Java的版本修改成8&#xff0c;也就是对应JDK1.8。

产品经理学习-策略产品指标

目录&#xff1a; 数据指标概述 通用指标介绍 Web端常用指标 移动端常用指标 如何选择一个合适的数据指标 数据指标概述 指标是衡量目标的一个参数&#xff0c;指一项活动中预期达到的指标、目标等&#xff0c;一般用数据表示&#xff0c;因此又称为数据指标&#xff1b;…

Oracle OCP怎么样线上考试呢

大家好&#xff01;今天咱们就来聊聊Oracle OCP这个让人又爱又恨的认证。为啥说又爱又恨呢&#xff1f;因为它既是IT界的“金字招牌”&#xff0c;又是一块硬骨头&#xff0c;不是那么容易啃下来的。好了&#xff0c;废话不多说&#xff0c;我们直奔主题&#xff0c;来看看关于…

阿里后端实习二面

阿里后端实习二面 记录面试题目&#xff0c;希望可以帮助到大家 类加载的流程&#xff1f; 类加载分为三个部分&#xff1a;加载、连接、初始化 加载 类的加载主要的职责为将.class文件的二进制字节流读入内存(JDK1.7及之前为JVM内存&#xff0c;JDK1.8及之后为本地内存)&…

C++:stack、queue、priority_queue增删查改模拟实现、deque底层原理

C:stack、queue、priority_queue增删查改模拟实现 前言一、Cstack的介绍和使用1.1 引言1.2 satck模拟实现 二、Cqueue的介绍和使用2.1 引言2.2 queue增删查改模拟实现 三、STL标准库中stack和queue的底层结构:deque3.1 deque的简单介绍(了解)3.2 deque的缺陷3.3 为什么选择dequ…

使用SpringBoot AOP记录操作日志和异常日志

使用SpringBoot AOP记录操作日志和异常日志 平时我们在做项目时经常需要对一些重要功能操作记录日志&#xff0c;方便以后跟踪是谁在操作此功能&#xff1b;我们在操作某些功 能时也有可能会发生异常&#xff0c;但是每次发生异常要定位原因我们都要到服务器去查询日志才能找…

Flask笔记

一&#xff1a;模板渲染 一般的话都序列化成字符串 二&#xff1a;项目拆分 2.1 项目拆分 app.py init.py views.py models.py 模型数据 2.2 蓝图 三&#xff1a;路由参数 3.1 String 重点 3.2 int 3.3 path 3.4 UUID 3.5 any 四&#xff1a;请求方式 五&#xff1a;Requ…

部署KVM虚拟化平台

文章目录 简介部署安装1、Centos6—3中&#xff0c;也加一块100G的硬盘&#xff0c;并在处理器上选择虚拟化2、内存给2个G3、分区fdisk -cu /dev/sdb -->n--p--1---回车--回车--w4、格式化为ext4格式5、建立文件&#xff0c;并把分区加到开机自启中6、挂在光盘7、安装图形化…

【vim 学习系列文章 3.1 -- vim 删除 ^M】

请阅读【嵌入式开发学习必备专栏 之 VIM 专栏】 文章目录 ^M 来源^M 删除 ^M 来源 在 Vim 中打开文件时&#xff0c;您可能会遇到行尾的 ^M 字符&#xff0c;这通常是因为文件使用了 Windows 风格的回车换行符&#xff08;CRLF&#xff09;&#xff0c;而不是 Unix/Linux 风格…

10.定时器各功能分析及编码

知识汇总&#xff1a; STM32的定时器有三种&#xff0c;高级定时器&#xff0c;通用定时器&#xff0c;基本定时器 就是功能多与少的差别&#xff0c;下面来逐个解释功能&#xff1a;在此之前&#xff0c;需要对几个概念有认知 几个概念&#xff1a; 1.定时器时钟频率&…