Spring Boot 项目打包之后一般都比较大,远程部署过程速度慢,而且浪费很多的资源

通过轻量化部署,将项目依赖分开打包,实际代码 jar 就会变得很小,可以快速部署到服务器上

使用assembly插件构建依赖

maven-assembly-plugin 是一个 Maven 插件,用于将项目及其依赖打包成一个可分发的归档文件(如 ZIP、TAR、JAR 等)。它通常用于创建包含所有依赖、配置文件和资源的完整发布包

添加依赖

<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-jar-with-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

创建描述文件

src/assembly/assembly.xml中定义打包的规则

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<!-- 定义构建的id,这个id会添加到生成文件名称的后缀符 -->
<id>make-jar-with-dependencies</id>

<!-- 定义打包格式,例如zip或tar.gz -->
<formats>
<format>zip</format>
<!-- &lt;!&ndash; 可以添加多个格式 &ndash;&gt;-->
<!-- <format>tar.gz</format>-->
</formats>

<!-- 依赖集,用于定义如何处理项目的依赖 -->
<dependencySets>
<dependencySet>
<!-- 依赖输出的目录 -->
<outputDirectory>lib</outputDirectory>
<!-- 是否包含项目本身的artifact -->
<useProjectArtifact>false</useProjectArtifact>
<!-- 是否解压缩依赖 -->
<unpack>false</unpack>
<!-- 第三方的依赖打包到lib目录下 -->
<excludes>
<exclude>
com.example*
</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>

多模块项目打包

如果项目是多模块项目,那么对于项目自身模块的jar包, 直接到 include打到打包后的jar中

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.demo.DemoApplication</mainClass>
<layout>ZIP</layout>
<includes>
<include>
<groupId>com.example</groupId>
<artifactId>base</artifactId>
</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>


项目打包

示例项目为拥有2个模块的maven项目,base模块为基础模块,business模块为业务模块,启动业务模块引用了基础模块

打包时候需要将base模块打包到jar中

项目目录如下

demo模块的pom如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>demo</name>
<description>demo</description>

<modules>
<module>base</module>
<module>business</module>
</modules>

<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

base模块的pom如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>base</artifactId>
<packaging>jar</packaging>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

</project>

business模块的pom文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>business</artifactId>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>base</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-jar-with-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.demo.DemoApplication</mainClass>
<layout>ZIP</layout>
<includes>
<include>
<groupId>com.example</groupId>
<artifactId>base</artifactId>
</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>

项目打包

mvn clean package

business模块打包后如下

business-0.0.1-SNAPSHOT.zip 压缩包里面包含了项目的依赖

构建之后的business-0.0.1-SNAPSHOT.jar也包含了base模块

spring-boot-jarmode-tools使用springboot优化分层结构的hjar包(在一些版本可能没有)

项目运行

需要执行项目依赖库

-Dloader.path=lib

启动命令如下

java "-Dloader.path=lib" -jar business-0.0.1-SNAPSHOT.jar

启动成功

Docker 支持

可以将项目的依赖库目录lib挂载到docker容器目录,

Dockerfile文件

# 使用官方 OpenJDK 17 作为基础镜像
FROM openjdk:17-jdk-alpine

# 设置工作目录
WORKDIR /app

# 将构建的 JAR 文件复制到容器中
COPY *.jar app.jar

# 暴露应用程序的端口(假设 Spring Boot 应用使用 8080 端口)
EXPOSE 8080

# 设置 JVM 参数(指定目录为/app/lib)
ENV JAVA_OPTS="-Xmx512m -Xms256m -Dloader.path=/app/lib"

# 启动应用程序
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

运行脚本

docker容器的启动如下

docker run -p 8080:8080 -v /data/lib:/app/lib my-spring-boot-app

-v /data/lib:/app/lib 将系统目录/data/lib映射到容器的/app/lib

只需要将依赖的jar解压到/data/lib即可

BACnet仿真

在开始学习 BACnet之前,我们先下载一个BACnet模拟器 Yabe (Yet Another BACnet Explorer)

下载地址可以点击此处 ,文章版本是 1.3.2

下载并安装

可以在导航栏看到

其中 BACnet.Room.Simulator 是BACnet设备仿真,可以让模拟现实中一个室内控制设备

打开软件,可以看到是一个温度控制传感器

左下角是预定的三种模式,分别是Comfort(舒适)、Eco+(环境友好)、Vacancv(空缺)

其实就是对应着不同的温度,例如图中选了1 Comfort,对应的温度是21°,此时室外温度是12° (分别是上面2个温度)

其中20.7°C则表示当前室内温度

底下是BACnet的设备Id:458942

Yabe扫描BACnet设备

简单了解之后,我们使用Yabe来扫描这个设备

右键点击Devices,然后选中 Add device

查看BACnet的一些链接参数

我们通过BACnet/IP V4方式连接

端口号使用即可,再选择一下IP地址(172.29.224.1 这是我本地的ip)

点击start,Yabe会在这个网段广播,扫描BACnet 设备

我们可以启动多个仿真,对应不同房间的设备

每个BACnet设备都有一个设备id

可以看到两台设备的设备id分别是 458942,458943

每个BACnet设备有一些寄存器地址(Address Space),用于存储不同的点位

BACnet基础对象类型

BACnet的设备字段根据类型可以分为

AI、AO、AV、BI、BO、BV、MI、MO、MV

类型 描述
AI 可读 不可 Analog Input
AO 可读 可写 Analog Output
AV 可读 可写 Analog Value
BI 可读 不可 Binary Input
BO 可读 可写 Binary Output
BV 可读 可写 Binary Value
MI 可读 不可 Multi State Input
MO 可读 可写 Multi State Output
MV 可读 可写 Multi State Value

除了 Input 为只读之外,其他都是可读可写

在 RoomControl 这个设备里,AI0 AI1 AI2 分别表示室内温度、热水温度、室外温度

室内温度

水温

室外温度

可以看到AI类型有以上这些字段

字段 描述
Description BACnet对象的字段description,用于进一步描述对象
Event State 事件状态
Object Identifier BACnet对象ID,格式为object-type,object-instance-number
Object Name BACnet对象的名称,用于描述对象的字段namedescription
Object Type 对象的BACnet类型,例如analog-input
Out Of Service 不可使用
Present Value 当前值属性
Reliability 可靠性
Status Flags 状态位
Units 单位

AV1 AV2 AV3 分别对应仿真的三个温度模式(Comfort/Eco+/Vacancv)的温度值

可以通过修改Present Value来改变仿真终端的数值

例如修改 SetPoint 1的值为31.1 ,可以看到终端上的温度也变成31.1

我们还可以将BACnet Object 拖拽到监听点位,在下方就可以数值变化的曲线,方便我们观察指标的变化

使用Java代码控制BACnet

我们本地使用迅绕的自控网关

在网关配置了BACnet IP 转发

点位如下

我们使用Java代码来读写点位

Maven 依赖

首先引入依赖

<dependency>
<groupId>org.code-house.BACnet4j</groupId>
<artifactId>ip</artifactId>
<version>1.3.0</version>
</dependency>

快速开始

编写一个简单测试方式来测试

这个例子使用IP直连的方式,而不是上面使用Yabe时用的广播方式

public class BACnetClientTest {
public static void main(String[] args) {
IpNetwork network = new IpNetworkBuilder()
.withSubnet("255.255.255.0", 24)
.withPort(47809)
.withReuseAddress(true)
.build();
int localDeviceId = RandomUtil.randomInt(10000);

LocalDevice localDevice = new LocalDevice(localDeviceId, new DefaultTransport(network));
BACnetClient client = new BACnetXClient(localDevice);
client.start();

int deviceId = 2605;

localDevice.send(IpNetworkUtils.toAddress("192.168.0.220", 47808), new WhoIsRequest(deviceId, deviceId));
RemoteDeviceFinder.RemoteDeviceFuture future = localDevice.getRemoteDevice(deviceId);

RemoteDevice remoteDevice = null;
try {
remoteDevice = future.get(3000L);
} catch (BACnetException e) {
e.printStackTrace();
}

DefaultDeviceFactory defaultDeviceFactory = new DefaultDeviceFactory();
Device device = defaultDeviceFactory.createDevice(remoteDevice);

BACnetObject BACnetObject = new BACnetObject(device, 0, BACnetObjectTypeEnum.ANALOG_INPUT.getType());
Object read = client.getPresentValue(BACnetObject, (BACnetToJavaConverter<Object>) encodable -> Double.parseDouble(encodable.toString()));
System.out.println("read " + read);
}
}

上面的程序主要是通过BACnet与远程设备通行,并读取对象的属性值

代码解释

  • 创建本地BACnet设备
IpNetwork network = new IpNetworkBuilder()
.withSubnet("255.255.255.0", 24) // 设置子网掩码和前缀长度
.withPort(47809) // 设置本地设备的端口号
.withReuseAddress(true) // 允许重用地址
.build();
int localDeviceId = RandomUtil.randomInt(10000); // 使用随机id

LocalDevice localDevice = new LocalDevice(localDeviceId, new DefaultTransport(network));
  • 启动BACnet客户端
BACnetClient client = new BACnetXClient(localDevice);
client.start(); // 启动客户端

这里BACnetXClient 是自己重新封装的,因为类库提供的没有提供LocalDevice,所以重新封装一个,源码在附录1

  • 发送WhoIsRequest发现远程设备
int deviceId = 2605;
localDevice.send(IpNetworkUtils.toAddress("192.168.0.220", 47808), new WhoIsRequest(deviceId, deviceId));

RemoteDeviceFinder.RemoteDeviceFuture future = localDevice.getRemoteDevice(deviceId);

RemoteDevice remoteDevice = null;
try {
remoteDevice = future.get(3000L); // 等待 3000 毫秒以获取远程设备
} catch (BACnetException e) {
e.printStackTrace();
}

这里提供远程设备的ip、端口和设备id

  • 创建Device对象

创建一个Device对象工厂,使用远程设备创建一个Device,用于读写操作

DefaultDeviceFactory defaultDeviceFactory = new DefaultDeviceFactory();
Device device = defaultDeviceFactory.createDevice(remoteDevice);
  • 读取远程设备的属性值
BACnetObject BACnetObject = new BACnetObject(device, 0, BACnetObjectTypeEnum.ANALOG_INPUT.getType());	// 创建一个BACnet的数据类型	
Object read = client.getPresentValue(BACnetObject, (BACnetToJavaConverter<Object>) encodable -> Double.parseDouble(encodable.toString())); // 读取属性值
System.out.println("read " + read);

本地使用BACnetObjectTypeEnum封装BACnet的数据类型

  • 写入远程设备的属性值
BACnetObject BACnetObject = new BACnetObject(device, 0,  BACnetObjectTypeEnum.ANALOG_OUTPUT.getType());				// 创建 AO 0 点
client.setPresentValue(BACnetObject, 3.0D, (JavaToBACnetConverter<Object>) e -> new Real((float) ((Double)e).doubleValue())); // 设置点位值为 3.0

设置之后

可以看到点位值变成3

说明点位写入成功

原理分析

代码原理如下图

在启动客户端 client.start() 之后,底层还先后执行transport和network两个实体的初始化方法 initialize ,并启动各自的内置线程

LocalDevice 的 initialize 方法

public synchronized LocalDevice initialize(RestartReason lastRestartReason) throws Exception {
this.deviceObject.writePropertyInternal(PropertyIdentifier.lastRestartReason, lastRestartReason);
this.timer = this.createScheduledExecutorService();
this.transport.initialize(); // 初始化 transport
this.initialized = true;

// 省略其他
}

Transport 的 initialize 方法

public void initialize() throws Exception {
this.servicesSupported = this.localDevice.getServicesSupported();
this.running = true;
this.network.initialize(this);
// 启动内置线程
this.thread = new Thread(this, "BACnet4J transport for device " + this.localDevice.getInstanceNumber());
this.thread.start();
// 向本地网络广播WhoIsRouter消息
this.network.sendNetworkMessage(this.getLocalBroadcastAddress(), (OctetString)null, 0, (byte[])null, true, false);
}

在 Transport 的 run 方法内处理 数据报文的出入 (Transport 实现 Runnable 接口 )

public void run() {
while(this.running) {
boolean pause = true;
Outgoing out = (Outgoing)this.outgoing.poll();
if (out != null) {
try {
out.send(); // 最终使用network发送请求
} catch (Exception var8) {}
pause = false;
}

NPDU in = (NPDU)this.incoming.poll();
if (in != null) {
try {
this.receiveImpl(in);
} catch (Exception var7) {}
pause = false;
}

// 省略部分代码
if (pause && this.running) {
ThreadUtils.waitSync(this.pauseLock, 50L); // 等待
}
}

}

Network 的 initialize 方法

public void initialize(Transport transport) throws Exception {
super.initialize(transport);
// 初始化 用户数据包套接字 UDP
this.localBindAddress = InetAddrCache.get(this.localBindAddressStr, this.port);

if (this.reuseAddress) {
this.socket = new DatagramSocket((SocketAddress)null);
this.socket.setReuseAddress(true);
if (!this.socket.getReuseAddress()) {
LOG.warn("reuseAddress was set, but not supported by the underlying platform");
}

this.socket.bind(this.localBindAddress);
} else {
this.socket = new DatagramSocket(this.localBindAddress);
}

// 启动线程
this.thread = new Thread(this, "BACnet4J IP socket listener for " + transport.getLocalDevice().getId());
this.thread.start();
}

在 Network 里面接收并处理数据报文

public void run() {
byte[] buffer = new byte[2048]; // 2M的缓存区
DatagramPacket p = new DatagramPacket(buffer, buffer.length);

while(!this.socket.isClosed()) {
try {
this.socket.receive(p); // 接收数据
this.bytesIn += (long)p.getLength();
ByteQueue queue = new ByteQueue(p.getData(), 0, p.getLength());
OctetString link = IpNetworkUtils.toOctetString(p.getAddress().getAddress(), p.getPort());
this.handleIncomingData(queue, link); // 处理数据
p.setData(buffer);
} catch (IOException var5) {
}
}

}

/**
* 将数据转成NPDU在放置到incoming队列
*/
protected void handleIncomingData(ByteQueue queue, OctetString linkService) {
try {
// 根据不同ip协议有不同实现来处理数据
NPDU npdu = this.handleIncomingDataImpl(queue, linkService);
if (npdu != null) {
this.getTransport().incoming(npdu);
}
} catch (Exception var4) {}

}

结论

通过本文学习,可以简单了解 BACnet 进行设备仿真、扫码设备、读写设备属性等基本操。通过 Java 代码进行于 BACnet设备进行交互,以及简单了解bacnet4j 基础类库的基本原理

附录1

BacNetXClient 实体类

import com.serotonin.bacnet4j.LocalDevice;
import com.serotonin.bacnet4j.type.constructed.ReadAccessResult;
import com.serotonin.bacnet4j.type.constructed.SequenceOf;
import org.code_house.bacnet4j.wrapper.api.BacNetClientBase;
import org.code_house.bacnet4j.wrapper.api.BacNetObject;
import org.code_house.bacnet4j.wrapper.api.Device;
import org.code_house.bacnet4j.wrapper.api.Type;


public class BacNetXClient extends BacNetClientBase {

public BacNetXClient(LocalDevice localDevice) {
super(localDevice);
}

@Override
protected BacNetObject createObject(Device device, int instance, Type type, SequenceOf<ReadAccessResult> readAccessResults) {

if (readAccessResults.size() == 1) {
SequenceOf<ReadAccessResult.Result> results = readAccessResults.get(0).getListOfResults();
if (results.size() == 4) {
String name = results.get(2).toString();
String units = results.get(1).toString();
String description = results.get(3).toString();
return new BacNetObject(device, instance, type, name, description, units);
}
throw new IllegalStateException("Unsupported response structure " + readAccessResults);
}
String name = getReadValue(readAccessResults.get(2));
String units = getReadValue(readAccessResults.get(1));
String description = getReadValue(readAccessResults.get(3));
return new BacNetObject(device, instance, type, name, description, units);
}

private String getReadValue(ReadAccessResult readAccessResult) {
// first index contains 0 value.. I know it is weird, but that's how bacnet4j works
return readAccessResult.getListOfResults().get(0).getReadResult().toString();
}

}

参考 org.code_house.bacnet4j.wrapper.ip.BacNetIpClient 开发的一个客户端,增加一个 BacNetXClient(LocalDevice localDevice) 的构造参数

tdengine2.4升到 tdengine3.0

代码改动

依赖升级

需要将taos的驱动包升级到3以上

<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>3.1.0</version>
</dependency>

然后处理代码

公司使用 mybatis 作为 tdengineORM 框架,所以 sql 编写在 XML 文件中
需要调整一下 sql 来兼容tdengine3

关键字处理

在字段上,我们使用到了 tdengine 的一些关键字,例如 value, index 等
需要在关键字前后添加反引号(`)

修改前

select last(value) from t_xxx;

修改后

select last(`value`) from t_xxx;

时间窗口

时间窗口函数interval在时序数据库的场景中用得比较频繁,
升级到 tdengine3 之后,无法与 group by 关键字一起使用,
需要替换成partition by
partition by在使用上与group by一样
partition语句放在where语句之后,interval语句之前

修改前

SELECT last(value), c_time, sensor_id 
FROM iot_table
interval(1m)
gorup by sensor_id

修改后

SELECT last(`value`), c_time, sensor_id 
FROM iot_table
partition by sensor_id
interval(1m)

数据迁移

使用 taos 自动的导入导出工具,可以很方便实现数据的迁移,下载链接

导出

在2.x版本,建议安装taosTools官方安装包,避免导出格式无法兼容3.x版本而出现一些错误
通过以下命令

taosdump -o export -D iot_db -T 8

-o 指定导出目录,需要先建立,不指定导出到当前目录,如果导出目录不为空,则会报错
-D指定导出的数据库,可以使用-A导出所有数据库
-T 指定线程大小,默认是8个线程

恢复数据

taosdump -i export

-i 指定导入目录

参考

  • 特色查询
  • taosdump

最近因为工作需要使用Python来开发和维护之前同事的代码
记录一下快速搭建python环境的过程

安装 Miniconda

MiniCondaconda的免费最小安装版,里面只有少量软件例如pip,zlib

要求window10或以上, 目前MiniConda的最新版自带Python版本是3.12

我们可以使用conda来管理我们的依赖,并通过切换环境来使用不同版本的Python

从页面点击下载, 下载页面为Minicondda download page

或者命令行 (window)

curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe -o miniconda.exe
start /wait "" miniconda.exe /S
del miniconda.exe

默认安装即可

安装完成后打开Anaconda Prompt

输入conda list 测试安装环境是否成功

更新镜像源(可选)

执行一下命令使用清华源

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes

执行完成后查看

conda config  --show

切换环境

默认的初始环境是base

创建环境

我们通过如下命令来创建一个环境

conda create --name env1 python=3.10

创建了一个名称叫env1的python3.10版本的环境
创建的时候会创建一个环境目录(在安装目录的env文件夹下)
并下载一些依赖,例如pip, zlib

下载的依赖

切换环境

通过命令

conda activate env1

就可以切换到我们刚刚创建的env1环境

可以看到python版本是3.10

停用环境

通过如下来停用环境

conda deactivate

安装依赖

我们也可以使用pip来安装依赖
但是推荐使用conda来安装

搜索依赖

conda search [-h] [--envs] [-i] [--subdir SUBDIR] [--skip-flexible-search] [-c CHANNEL] [--use-local]
[--override-channels] [--repodata-fn REPODATA_FNS] [--experimental {jlap,lock}]
[--no-lock] [--repodata-use-zst | --no-repodata-use-zst] [-C] [-k] [--offline] [--json]
[-v] [-q]

支持很多参数, 并且支持MatchSpec查询方式(conda查询语言)

普通查询

conda search numpy

![image-20240508190855795](images/python-quick-start/image-20240508190855795.png)

查询某个版本范围

conda search "numpy>=1.26"

模糊搜索

conda search "*mysql*"

支持*模糊匹配

查看某个版本的具体信息

conda search pandas[build=py39h5da7b33_0]  -i

安装依赖

conda install scipy

下载scipy到当前环境

可以通过-n--name来指定环境

conda install -n env1 scipy
conda install --name env1 scipy

env1是环境名称

指定版本

conda install numpy=1.21.5 -y

-y 是跳过询问,直接下载

查看依赖

conda list

可以查看环境的依赖包有哪些

安装 Pycharm

进过上面环境的安装之后

我们已经可以编写python代码啦

工欲善其事,必先利其器

世界上有家做IDE很牛逼的公司叫JetBrains

使用PyCharm来开发,下载地址

使用免费的社区版就足矣了

配置环境

为了使不同项目的依赖不冲突,我们可以新建一个项目,并创建一个conda环境

进入到配置页面

依次点击 Project -> Python Interpreter -> Add Interpreter -> Add Local Interpreter

然后新增一个conda环境,如下

接着我们就可以在这个环境开发了

开发一个最小WEB程序

创建一个新环境, web-hello-world

使用python版本为3.11

按照flasky依赖

pip install Flash

安装完成

编写一个Flask程序

文件hello.py

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"

启动项目

flask --app hello run

访问127.0.0.1:5000

运行成功

开发一个爬虫项目

我们根据上面的步骤创建一个新的环境

创建一个新的项目 spider-demo

安装依赖

pip install BeautifulSoup4 requests

编写一个爬虫抓取古诗

from bs4 import BeautifulSoup
import requests

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36'
}
url = 'https://www.gushicimingju.com/gushi/shi/7.html'
response = requests.get(url, headers=headers)

soup = BeautifulSoup(response.text, 'html.parser')

contentList = soup.find(class_="gushi-info").find(class_="shici-content").select('p')

for item in contentList:
print(item.getText())

运行如下

总结

结合Pycharm来使用可以大大提高开发的效率

针对不同的环境也自由切换成本也很低

依赖库的版本冲突问题也可以解决

参考

  • miniconda
  • switch-environment
  • pycharm configure conda environment
0%