概述
主要用yothon实现:模拟生成新能源车辆数据(当然,其他语言也是一样的,思路都差不多,只是语法稍有改变),主要用于测试一些代码或一些测试步骤(例如将数据上传到HDFS上等),当然对于像我这样的小白用来练手也是一个不错的选择。
基本要求:
- 数据形式:
模拟生成当天的新能源车辆数据(字段信息必须包含:车架号、行驶总里程、车速、车辆状态、充电状态、剩余电量SOC、SOC低报警、数据生成时间等)
- 要求:
1、最终部署时,将数据写入hdfs中(如果没有在虚拟机上部署Hadoop的话省略,将数据写入本地即可)
2、车辆数据要按天存储,数据格式是JSON格式,另外如果数据文件大于100M,则另起一个文件存。每天的数据总量不少于300M。比如假设程序是2023-01-1 运行,那么就将当前模拟生成的数据写入到HDFS的/can_data/2023-01-01文件夹的can-2023-01-01.json文件中,写满100M,则继续写到can-2023-01-01.json.2文件中,依次类推;
3、每天模拟生成的车辆数据中,必须至少包含20辆车的数据,即要含有20个车架号(一个车架号表示一辆车,用字符串表示);
4、每天生成的数据中要有少量(20条左右)重复数据(所有字段都相同的两条数据则认为是重复数据),且同一辆车的两条数据的数据生成时间间隔两秒;
5、每天生成的数据中要混有少量前几天的数据(即数据生成时间不是当天,而是前几天的)。
实现思路
可以从题中分析,其实整体的可以分为两大部分生成数据和写入数据
1、生成数据
以下是我本人对数据生成的一些实现思路,其实模拟的并是很好,因为车辆在运行中时,车速是不可变化莫测的,想要很好的模拟车速还是比较困难。
假设: 只有车启动(或充电)时才会开始上传数据
- 思路:
1、可以创建一个类,这个类接收一个车架号,用于生成一辆车一天的数据。
2、随机获取一个时间节点(use_car_time),作为本次用车(充电)的时间,就是相当于规定本次用车时长。
3、再随机获取一个时间节点,作为车辆启动的时间节点,从这个时间点开始每2秒生成一辆车的数据
4、随机初始化行程里程,行程里程随着车辆的运行而增加
5、随机初始化电量,电量随着车辆的变化而变化
6、确定当天的日期,让其在今天和前几天中选择,但被选中的几率极低,从而达到混入前几天的数据
7、初始化低电预警,当电量低于30%则提示:电量低于30%
以上部分我称为初始化车辆数据,在生成数据的逻辑中,至关重要。
以下是生成数据的逻辑:
其实就是通过不断的判断车辆的状态,来生成对应的数据,逻辑其实并不复杂,但是需要梳理清楚
注意:数据要随着状态的变化而变化
1.1 rondom模块
生成随机数据主要通过Rondom模块来实现,以下会对rondom模块进行简单介绍,如果已经有过了解即可跳过。
random
模块是 Python 标准库中用于生成随机数的模块,它提供了众多的随机数生成函数。
下面是一些常用的 random
模块函数:
random.random()
:返回一个 0 到 1 之间的随机浮点数。random.randint(a, b)
:返回一个 a 到 b 之间的随机整数。random.choice(seq)
:从序列seq
中随机选择一个元素。random.choices(seq, weights=None, cum_weights=None, k=1)
:从序列seq
中随机抽取元素的函数random.shuffle(x)
:将序列x
随机打乱顺序,返回打乱后的序列。random.sample(population, k)
:从总体population
中抽取k
个不重复的元素出来,返回这k
个元素构成的列表。
示例代码如下,演示了使用 random
模块生成随机数的基本用法:
import random
# 生成 0 到 1 之间的随机浮点数
print(random.random())
# 生成 1 到 10 之间的随机整数
print(random.randint(1, 10))
# 从列表中随机选择一个元素
items = ['apple', 'banana', 'orange']
print(random.choice(items))
# 打乱一个列表中的元素顺序
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)
print(numbers)
# 从总体中随机抽取 3 个不重复的元素
population = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(random.sample(population, 3))
上述代码分别演示了生成随机浮点数、随机整数、随机选择列表元素、打乱列表元素顺序和随机抽样等基本用法。
重点讲一讲random.choices()
random.choices()
random.choices(seq, weights=None, cum_weights=None, k=1)
是 random
模块中用于从序列中随机抽取元素的函数。其中,seq
表示需要抽取元素的序列,k
表示需要抽取的元素个数,可以通过调整 k
的值来实现有放回或无放回采样。
除了必选参数 seq
和 k
之外,random.choices()
函数还提供了两个可选参数:
weights
:表示每个元素对应的权重,默认情况下,所有元素的权重相等。cum_weights
:与weights
参数一样,表示每个元素对应的累计权重。
如果提供了 weights
参数,则 random.choices()
函数会按照提供的权重值进行采样;如果提供了 cum_weights
参数,则 random.choices()
函数会按照提供的累计权重值进行采样。
下面的示例展示了使用 random.choices()
函数进行随机采样的基本用法:
import random
# 从字符串中随机抽取一个字符
s = "abcdefg"
print(random.choices(s))
# 从列表中随机抽取 3 个元素,每个元素的权重分别为 2、3、5
lst = ["A", "B", "C"]
w = [2, 3, 5]
print(random.choices(lst, weights=w, k=3))
上述代码分别演示了从字符串中随机抽取一个字符,以及从列表中随机抽取 3 个元素。在第二个示例中, weights
参数指定了每个元素的权重,即选 A 的概率为 2/10,选 B 的概率为 3/10,选 C 的概率为 5/10。最后输出的结果才按照这个概率来输出。
1.2模拟生成新能源车辆数据代码演示(python)
需要注意的是:输出的是一个二维列表,子列表表示一次用车所产生的数据
需要多少辆车的数据传入多少个车架号即可
import random
import datetime
import json
def get_vin(number: int) -> list:
"""
用来生成指定数量的车架号
param:number 生成多少量车的数据
return: 返回一个列表
"""
vin_set = set() # 创建一个空集合
for i in range(number):
vin = ''.join(random.choices(
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', k=17)) # 车架号
if vin not in vin_set:
vin_set.add(vin)
return list(vin_set)
class GenerationData:
def __init__(self, vin):
self.vin = vin
self.mileage = round(random.uniform(200, 50000), 2) # 行驶总里程
# 随机初始化电量
self.soc = round(random.uniform(10, 100), 2) # 剩余电量SOC
# 需要混入少量前几天的数据(让其在今天和前几天中选择,但被选中的几率极低)
self.now_day = random.choices([(datetime.datetime.now() - datetime.timedelta(days=random.randint(1, 3))),
datetime.datetime.now()], weights=[0.05, 0.95])[0]
# 随机选取司机用车时间(或充电的时间)
self.use_car_time = \
random.choices([self.get_random_date(0, 23), self.get_random_date(8, 11), self.get_random_date(16, 22)],
weights=[0.2, 0.4, 0.4])[0] # 模拟这个时间段用车的人较多
# 低点预警
self.soc_low_alarm = '电量正常' if self.soc >= 30 else '电量低于30%'
# 用于保证数据为当天的数据
self.time_compare = datetime.datetime.combine(self.now_day.date(), datetime.time(23, 59, 50))
@staticmethod
def get_random_speed():
"""
用来随机获取车速
"""
speed1 = random.randint(0, 60)
speed2 = random.randint(61, 90)
speed3 = random.randint(91, 120)
speed = random.choices([speed1, speed2, speed3], weights=[0.6, 0.3, 0.1])[0]
return speed
def get_random_date(self, start: int, end: int):
"""
用来生成当天的随机时间
param:start 起始时间(小时)
param:end 结束时间(小时)
return: 当天的随机时间
"""
# 生成随机的小时数、分钟数和秒数,并构造时间对象
random_time = datetime.time(random.randint(
start, end), random.randint(0, 59), random.randint(0, 59))
# 构造完整的时间对象,包含日期和时间
random_datetime = datetime.datetime.combine(self.now_day.date(), random_time)
return random_datetime
@staticmethod
def get_travel_time():
"""
随机获取一个随机时间(利用随机权重来设置)
"""
random_hour_list = [0, 1, 2, random.randint(3, 5), random.randint(5, 8), random.randint(8, 10),
random.randint(10, 15), random.randint(15, 23)]
random_hour = random.choices(random_hour_list, weights=[
0.7, 0.3, 0.2, 0.15, 0.1, 0.05, 0.03, 0.01])[0]
random_time = datetime.time(
random_hour, random.randint(0, 59), random.randint(0, 59))
return random_time
def if_not_charge(self, choose_charge_or_not):
data_list = []
steer_time = self.get_travel_time() # 随机获取一个驾驶时间
print("司机本次开车", steer_time)
seconds = (steer_time.hour * 60 + steer_time.minute) * 60 + steer_time.second # 将驾驶时间转换为秒数
flag = 0 # 立一个旗帜来判断是否达到驾驶时间
print("司机的用车时间为:", self.use_car_time)
while flag <= seconds:
speed = self.get_random_speed() # 随机生成0-130的车速,但侧重于0-60的车速
if speed == 0: # 模拟行驶中可能会停住
status = '停车中' # 设置车辆状态
for i in range(random.randint(0, 50)): # 模拟停车
self.use_car_time += datetime.timedelta(seconds=2) # 每两秒更新一次时间
if self.use_car_time > self.time_compare: # 保证生成的是今天的数据
break
data = {
'车架号': self.vin,
'行驶总里程': self.mileage,
'车速': speed,
'车辆状态': status,
'充电状态': choose_charge_or_not,
'剩余电量SOC': self.soc,
'SOC低报警': self.soc_low_alarm,
'数据生成时间': self.use_car_time.strftime('%Y-%m-%d %H:%M:%S')
}
data_list.append(data)
flag += 2
else:
status = '行驶中'
# 假设每两秒随机跑10-60米
run_mileage = random.uniform(0.01, 0.06) # 因为速度是随机生成的,计算起来会不符合逻辑,那么这两秒跑了远也随机吧
self.mileage += run_mileage # 更新行驶里程
consume = run_mileage * 0.1 # 消耗电量
self.soc -= consume # 更新剩余电量
if self.soc <= 10: # 电量小于10停止行驶
break
self.soc_low_alarm = '电量正常' if self.soc > 30 else '电量低于30%' # 低电提示
# 加上时间差2秒
self.use_car_time += datetime.timedelta(seconds=2)
if self.use_car_time > self.time_compare: # 保证生成的是今天的数据
break
data = {
'车架号': self.vin,
'行驶总里程': self.mileage,
'车速': speed,
'车辆状态': status,
'充电状态': choose_charge_or_not,
'剩余电量SOC': self.soc,
'SOC低报警': self.soc_low_alarm,
'数据生成时间': self.use_car_time.strftime('%Y-%m-%d %H:%M:%S')
}
data_list.append(data)
flag += 2
print("本次用完车后的时间:", self.use_car_time)
return data_list
def if_charge(self, choose_charge_or_not):
data_list = []
status = '停车中' # 默认停车中
speed = 0 # 车速=0
# 这时候就要考虑车主要充多电多久(随机生成充电时间)
charge_timme = self.get_travel_time()
print("司机本次充电时间", charge_timme)
seconds = (charge_timme.hour * 60 + charge_timme.minute) * 60 + charge_timme.second # 将充电时间转换为秒数
flag = 0
print("司机开始充电时间为:", self.use_car_time)
print("未充电前电量为:", self.soc)
if self.soc < 90: # 如果此时车的电量是大于90的,那么就没有充电的必要了
while flag <= seconds:
if self.soc < 100: # 确保电量不会超过100
self.soc_low_alarm = '电量正常' if self.soc > 30 else '电量低于30%' # 低电提示
self.soc += 2 * (1 / 60) # 充电效率跟充电桩有一定因素,暂不考虑太多,就假设1分钟充1%的电
self.use_car_time += datetime.timedelta(seconds=2) # 每两秒更新一次时间
if self.use_car_time > self.time_compare: # 保证生成的是今天的数据
break
data = {
'车架号': self.vin,
'行驶总里程': self.mileage,
'车速': speed,
'车辆状态': status,
'充电状态': choose_charge_or_not,
'剩余电量SOC': self.soc,
'SOC低报警': self.soc_low_alarm,
'数据生成时间': self.use_car_time.strftime('%Y-%m-%d %H:%M:%S')
}
data_list.append(data)
flag += 2
else:
break
print("司机结束充电时间为:", self.use_car_time)
print("充电结束后电量为:", self.soc)
return data_list
def choose_whether_charge(self):
charging = ['未充电', '充电中']
if self.soc < 30:
choose_charge_or_not = random.choices(charging, weights=[0.2, 0.8])[0] # 如果电量小于30,则选择充电的就有80%的几率选择充电
else:
choose_charge_or_not = random.choices(charging, weights=[0.8, 0.2])[0] # 反之就有80%选择不充电
return choose_charge_or_not
def generation_data(self):
data_list = []
# 选择是充电
choose_charge_or_not = self.choose_whether_charge()
while True:
if self.use_car_time > self.time_compare: # 保证生成的是今天的数据
break
if choose_charge_or_not == '未充电':
list1 = self.if_not_charge(choose_charge_or_not)
data_list.append(list1) # 将列表合并
# 退出循环即熄火,熄火之后又面临后续是否还需要用车
# 有三种可能:在随机时间后用车,在随机时间后充电,今天不在用车
if self.soc < 30:
weights = [0.1, 0.5, 0.4]
else:
weights = [0.5, 0.1, 0.4]
choose_may = random.choices(['随机时间后用车', '随机时间后充电', '今天不在用车'], weights=weights)[0]
if choose_may == '随机时间后用车': # 随机时间后用车,就将司机上次用完车后的时间+上随机时间
random_time = self.get_travel_time()
print(random_time, "后用车")
seconds = (random_time.hour * 60 + random_time.minute) * 60 + random_time.second
self.use_car_time += datetime.timedelta(seconds=seconds) # 更新下次用车时间
continue # 结束本次循环,等待下次用车
elif choose_may == '随机时间后充电':
choose_charge_or_not = '充电中' # 修改充电状态
random_time = random.randint(10, 500)
print(random_time, "后用充电")
self.use_car_time += datetime.timedelta(seconds=random_time) # 更新下次用车时间
if self.use_car_time > self.time_compare: # 保证生成的是今天的数据
break
continue # 结束本次循环,等待下次用车
else:
print("今天不再用车")
break
else: # 这种情况:车在充电
list2 = self.if_charge(choose_charge_or_not)
data_list.append(list2) # 将列表合并
# 充好电后,之后又面临后续是否还需要用车
# 有两种可能:随机时间后用车,今天不在用车
choose_may = random.choices(['随机时间后用车', '今天不在用车'], weights=[0.7, 0.3])[0]
if choose_may == '随机时间后用车': # 随机时间后用车,就将司机上次用完车后的时间+上随机时间
choose_charge_or_not = '未充电' # 修改充电状态
random_time = self.get_travel_time()
print(random_time, "后用车")
seconds = (random_time.hour * 60 + random_time.minute) * 60 + random_time.second
self.use_car_time += datetime.timedelta(seconds=seconds) # 更新下次用车时间
if self.use_car_time > self.time_compare: # 保证生成的是今天的数据
break
continue # 结束本次循环,等待下次用车
else:
print("今天不在用车")
break
return data_list
if __name__ == '__main__':
vins_list = get_vin(1) # 生成50辆车的数据
vin1 = vins_list.pop() # 随机取出一辆,先模拟一辆车
gd = GenerationData(vin1) # 创建对象
# 生成数据
data_li = gd.generation_data()
print(len(data_li))
2、将生成的数据存储到hdfs
无论存储到本地还是存储到hdfs,基本思路其实都是差不多的。小编这里两个都会演示。
- 首先要部署到hdfs上的话,需要在虚拟机上部署Hadoop,如果没有部署,可以直接跳过了,可以试试将数据存储到本地,基本思路都差不多,但需要用到os库。
- 如果对hdfs库中InsecureClient模块不熟悉的,可以看看我的主页,会有一期讲解InsecureClient模块的使用,主要介绍python模块对hdfs的增删改查等一系列简单操作
- 本例中会用到
2.1连接上web端的hdfs
- 下载 hdfs 库
pip install hdfs
- 导入模块
from hdfs import InsecureClient
InsecureClient是hdfs模块中提供的一种客户端对象,它可以通过安全和非安全两种方式连接到HDFS集群。
- 创建连接
client = InsecureClient(‘http://192.168.88.129:50070’, user=‘root’)
在本例中,我们使用的是非安全方式连接HDFS,因此使用了InsecureClient作为客户端对象
- 第一个参数:'http://192.168.88.129:50070’是HDFS服务的访问地址。在Hadoop集群中,HDFS服务一般运行在一个或多个节点上,通过这个地址可以访问HDFS服务提供的Web UI界面。
- user:用于指定客户端的用户名,这个用户名需要在HDFS中有相应的权限。
2.2 代码中会用到的InsecureClient模块中的方法
一个简单的表格是这么创建的:
方法 | 作用 |
---|---|
makedirs | 用于创建指定路径的目录,可以同时设置目录的权限如果指定的路径已经存在,则会抛出HdfsError 异常。 |
status | 获取文件或目录的详细信息(本例中会用来判断文件是否存在) |
content | 列出指定路径的文件或目录的详细内容信息(本例中会用来查看文件的大小) |
write | 将数据写入到指定的 HDFS 文件路径 |
2.3 参数介绍
makedirs()
基本参数
client.makedirs
函数的参数包括:
hdfs_path
: 要创建的HDFS路径。必选参数。permission
: 创建目录时指定的权限。默认值为None
,表示使用默认的权限。如果设置了权限,则必须是一个八进制数字符串,例如"755"
表示创建的目录权限为rwxr-xr-x
。
例如,要创建/test
目录,并设置其权限为755
,可以使用以下代码:
from hdfs import InsecureClient
client = InsecureClient('http://192.168.88.129:50070', user='root')
# 创建/test目录,权限设置为755
client.makedirs('/test', permission=755)
在上述代码中,client.makedirs
函数指定了要创建/test
目录,并设置目录的权限为755
。如果不指定权限,默认使用HDFS的默认权限。
需要注意的是,如果指定的路径已经存在,则会抛出HdfsError
异常。在使用client.makedirs
函数前,需要确保指定的路径不存在,并且参数的设置符合预期。
status()
基本参数:
client.status
函数的基本参数如下:
hdfs_path
: 需要获取信息的文件或目录路径。必选参数。strict
: 是否开启严格模式。默认为True
。如果开启严格模式,当路径不存在时会抛出HdfsError
异常。如果关闭严格模式,路径不存在时会返回一个空的dict
对象。
在实际使用中,我们一般只需要指定hdfs_path
参数,获取文件或目录的详细信息即可。需要注意的是,client.status
函数返回的是一个dict
对象,其中包含了文件或目录的详细信息。可以通过取字典中的某个键的方式,获取对应的值。例如:
from hdfs import InsecureClient
client = InsecureClient('http://192.168.88.129:50070', user='root')
# 文件路径
hdfs_path = '/car_data/2023-05-29/car-2023-05-29.json'
file_info = client.status(hdfs_path
print(file_info['length']) # 打印文件的大小
content()
基本参数
client.content
函数的参数包括:
hdfs_path
: 要列出的HDFS路径。必选参数。strict
: 是否开启严格模式,当开启严格模式时,如果目录或文件不存在,将抛出HdfsError
异常。默认值为False
。
例如,要列出根目录/
的内容信息,开启严格模式,可以使用以下代码:
from hdfs import InsecureClient
client = InsecureClient('http://192.168.88.129:50070', user='root')
# 文件路径
hdfs_path = '/'
content_info = client.content(hdfs_path=hdfs_path, strict=True)
print(content_info)
在上述代码中,client.content
函数指定了要列出/
目录的内容信息,并且设置了strict=True
,表示开启严格模式,如果目录不存在将抛出异常。最后,将目录的内容信息打印出来。
需要注意的是,如果指定的路径不存在,且未开启严格模式,将返回None
。如果开启了严格模式,则在指定的路径不存在时会抛出异常。在使用client.content
函数前,需要确保指定的路径存在,并且参数的设置符合预期。
write()
常用参数介绍:
path
(必选):表示 HDFS 文件系统的文件路径,如果包含文件名,则需要在文件名前添加目录名,例如 ‘/dir/filename’ 表示将数据写入到 HDFS 文件系统中的 ‘/dir’ 目录下的 ‘filename’ 文件中;data
(必选):表示要写入文件的数据,必须是 bytes 类型;overwrite
(可选):表示是否覆盖已经存在的文件,默认为 False,即不覆盖;append
(可选):表示是否将数据追加到已经存在的文件中,默认为 False,即不追加;blocksize
(可选):表示要写入的数据块的大小,默认为 None(自动调整块大小);replication
(可选):表示写入数据时使用的文件副本数,默认为 None。
示例代码如下,假设要将数据写入到 hdfs://namenode:8020/dir/filename 文件中:
from hdfs import InsecureClient
# 创建连接到 HDFS 的客户端实例
client = InsecureClient('http://namenode:50070')
# 要写入的数据
data = b'Hello, world!'
# 向 HDFS 写入数据
with client.write('/dir/filename', overwrite=True) as writer:
writer.write(data)
上述代码中,首先创建了一个连接到 HDFS 的客户端实例,然后准备要写入的数据。接下来使用 client.write()
方法向指定路径 ‘/dir/filename’ 的文件中写入数据,同时开启了覆盖已有文件的选项。由于代码中使用了 with
语句块,write()
方法会返回一个文件写入器(file writer)对象 writer
,然后在语句块中向该对象中写入数据,最后退出语句块时,该对象会自动关闭,提交数据块到 HDFS。写入的数据即为 b’Hello, world!'。
2.4、实现思路
明确目的:
- 一边生成数据一边写入数据
- 每次写入5条数据,并判断文件大小是否超出阈值(100M 相当于 100 * 1024 * 1024)的测试的时候改小一点
- 写入的同时,随机添加一些重复数据,来实现题目要求
- 每当超出阈值创建新的文件写入
实现的基本步骤:
2.5 将数据写入hdfs代码实现(python)
from hdfs import InsecureClient
from generation_data import GenerationData
import random
import json
import datetime
def get_vin(number: int) -> list:
"""
用来生成指定数量的车架号
:param:number 生成多少量车的数据
:return: 返回一个列表
"""
vin_set = set() # 创建一个空集合
for i in range(number):
vins = ''.join(random.choices(
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', k=17)) # 车架号
if vins not in vin_set:
vin_set.add(vins)
return list(vin_set)
def create_folder_if_not_exists(file_path):
"""
判断文件是否存在,不存在则创建
:param file_path:文件位置
:return:
"""
if client.status(file_path, strict=False) is None:
client.makedirs(file_path, permission=755) # 创建目录并将数据修改权限为755
print(f"目录{file_path}创建成功!")
else:
print("目录已存在")
def add_repeating_data(data_list: list):
"""
用于向列表中添加重复数据
随机添加5-15条不等
列表内的数据需要是字典
:param data_list: 需要添加重复数据的列表
:return:无返回值,对传进来的数据进行隐形操作
"""
# sample()函数从0到列表总长度之间随机选择random.randint(20,100)整数作为重复的数据下标
try:
repeat_indices = random.sample(range(len(data_list)), random.randint(5, 15))
print(repeat_indices)
for i in repeat_indices:
# 返回字典中所有键值对的元素,以便同时访问字典中所有键和它们对应的值
repeat_data = {k: v for k, v in data_list[i].items()}
print(data_list[i], "下面添加了:", repeat_data)
data_list.insert(i + 1, repeat_data) # 将数据插入原数据的下一行
except Exception as e:
print("添加失败:", e)
def split_data(one_vin_data, null_list: list) -> list:
"""
并随机添加行程重复数据
拆分传入的列表数据,将列表数据拆分为,5个数据为一个子列表
:param:one_vin_data:一辆车架号的列表数据
:param:null_list:一个空列表
:return: 二维列表(每个列表5个数据)
"""
n = 5 # 每个子列表包含10个元素
for tmp_list in one_vin_data:
"""
随机选择本次行程是否会出现重复数据,出现:添加5-20条数据
用于选择是否出现重复数据,出现的概率为3%
出现返回True
不出现返回False
"""
flag = random.choices([True, False], weights=[0.03, 0.97])[0]
if flag: # 出现:添加5-15条数据
add_repeating_data(tmp_list)
# 将数据拆分为10个数据为一个小列表
for i in range(0, len(tmp_list), n):
null_list.append(tmp_list[i:i + n])
return null_list
if __name__ == '__main__':
client = InsecureClient('http://192.168.88.129:50070', user='root')
NOW_DATA_STR = datetime.datetime.now().date().strftime('%Y-%m-%d') # 当天日期
FILE_PATH = f'/car_data/{NOW_DATA_STR}' # 需要的目录
# threshold = 100 * 1024 * 1024 # 文件大小阈值为100MB
threshold = 100000 # 设置文件阈值
index = 2 # 文件后缀
# 创建文件夹
create_folder_if_not_exists(FILE_PATH)
data_path = FILE_PATH + f'/car-{NOW_DATA_STR}.json' # 初始化文件地址
# 生成车架号
vin = get_vin(1)
# 循环写入数据 -> 5条数据5条数据的写
for i in range(len(vin)):
tmp_list = [] # 用一个临时列表来临时存储数据
# 生成数据
gd = GenerationData(vin[i])
one_vin_data_list = gd.generation_data() # 生成一辆车的数据
# 随机添加重复数据,并切割数据列表,所有的子列表中有5个数据
data_list = split_data(one_vin_data_list, tmp_list) # -> 二维列表
# 循环子列表
for son_list in data_list: # 一个列表一个列表的写入
# 判断文件是否存在,不存在则返回None
if client.status(data_path, strict=False):
# 文件存在,追加内容
with client.write(data_path, append=True, encoding='utf-8') as writer:
# client.set_permission(data_path, permission='777') # 修改文件权限
for data in son_list:
# 转为json数据
data = json.dumps(data, ensure_ascii=False)
# 将编码后的字符串写入文件中,并添加换行符
writer.write(data + '\n')
writer.flush() # 立即将缓存的数据全部写入文件
file_info = client.content(data_path) # 获取文件信息
file_size = file_info['length'] # 获取文件大小
# print(file_size)
if file_size > threshold: # 如果文件大于阈值则更新文件地址
data_path = FILE_PATH + f'/car-{NOW_DATA_STR}.json.{index}'
index += 1
else: # 不存在则开一个新的文件
with client.write(data_path, encoding='utf-8') as writer:
for data in son_list:
# 转为json数据
data = json.dumps(data, ensure_ascii=False)
# 将编码后的字符串写入文件中,并添加换行符
writer.write(data + '\n')
writer.flush() # 立即将缓存的数据全部写入文件
3、将数据写入本地
基本思路的实现思路与写入hdfs差不多。所以就不在多说。
需要用到os库
3.1 简单介绍os库
os 模块是 Python 语言中一个常用的操作系统相关库,通过该模块可以完成许多涉及文件、目录、进程等的操作。
主要的功能包括:
- 操作文件和目录:如创建、重命名、删除、移动、复制等文件和目录操作;
- 进程管理:如获取进程 ID、杀死进程、调用新进程等操作;
- 系统信息:如获取系统版本、CPU 数量、内存使用情况等信息;
- 文件描述符操作:如打开、关闭文件,读取、写入数据等操作;
- 环境变量操作:如获取、设置、删除环境变量。
os 模块中的函数、方法和常量很多,主要包括以下几个常用的部分:
- 文件和目录操作相关函数:os.getcwd(),os.listdir(),os.mkdir(), os.rmdir(),os.path.join(),os.path.dirname(),os.path.basename() 等;
- 进程管理相关函数:os.fork(),os.kill(),os.wait() 等;
- 系统信息相关函数:os.name,os.environ 等;
- 文件描述符操作函数:os.open(),os.close(),os.read(),os.write() 等。
3.2 本次案例会用的到方法
方法 | 作用 |
---|---|
os.mkdir() | 在指定的路径中创建新的目录。 |
os.makedirs() | 递归创建多层目录 |
os.path.exists() | os.path 模块中的函数,其作用是用于判断指定的路径是否存在,如果路径存在则返回 True,否则返回 False |
os.path.getsize() | 用于获取指定文件的大小,单位为字节 |
3.3 基本参数
os.mkdir()
函数的主要参数如下:
path
:必选,表示要创建的目录路径。这是一个字符串,可以包含多级文件夹,例如"C:\\data\\temp\\images"
,其中路径分隔符可以使用 “” 或 “/”。mode
:可选,表示新目录的权限。默认值为 0o777,一般不需要指定,除非你需要创建一个权限不同的目录。在 Unix/Linux 中,mode
表示新建目录的文件权限掩码(umask);在 Windows 中,忽略mode
参数,使用操作系统的默认权限。
当运行 os.mkdir
函数时,程序会尝试在指定路径下创建新目录,如果该目录不存在,则会创建成功,并返回 None
;如果该目录已经存在,则会抛出 OSError
异常,并提示目录已经存在。
示例代码如下,用于在当前工作目录下创建一个名为 “example” 的新目录:
import os
# 创建一个新目录
os.mkdir('example')
上述代码实现了在当前工作目录下创建名为 “example” 的新目录。
os.makedirs()
函数的主要参数如下:
name
:必选,表示要创建的目录路径,可以是一个字符串类型的路径,也可以是一个包含多个路径的列表类型。mode
:可选,表示新目录的权限,默认值为 0o777。exist_ok
:可选,表示如果目录已经存在时是否抛出异常,默认为False
,即目录已存在时会抛出异常,设为True
时,不会抛出异常。
当运行 os.makedirs()
函数时,程序会尝试在指定路径下递归创建所有目录层次。如果路径对应的目录已经存在,则不会抛出异常。如果路径中的某个目录不存在,则会创建该目录,直到创建所有目录为止。
示例代码如下,使用 os.makedirs()
在当前目录下递归创建多层目录:
import os
# 递归创建目录
os.makedirs('./data/input', exist_ok=True)
上述代码实现了在当前目录下递归创建了 ./data/input
两层目录。
os.path.exists()
参数:
参数 path
表示需要检查是否存在的路径。该函数会在执行时检查指定的路径是否存在,如果路径存在则返回 True
,否则返回 False
。
示例代码如下,用于检查指定路径是否存在:
import os
# 判断指定路径是否存在
if os.path.exists('./data/input'):
print('指定路径存在')
else:
print('指定路径不存在')
上述代码会先判断 ./data/input
目录是否存在,如果存在,则输出 "指定路径存在"
,否则输出 "指定路径不存在"
。
os.path.exists()
函数没有任何可选参数,只有一个必选参数 path
,表示需要检查的路径。
os.path.getsize()
函数的语法:
os.path.getsize(path)
其中,参数 path
表示需要获取大小的文件路径。该函数会在执行时获取指定文件的大小,并以字节的形式返回文件的大小。
示例代码如下,用于获取指定文件的大小:
import os
# 获取文件大小
size = os.path.getsize('./data/example.txt')
print('文件大小为', size, '字节')
上述代码会获取 ./data/example.txt
文件的大小,并输出文件大小,单位为字节。
需要注意的是,os.path.getsize()
函数只能获取文件的大小,如果传入的参数是目录路径,则会抛出 OSError
异常。
总结
写入本地就不做代码演示了,只要明确思路,其实还是写起来也并不困难。可以用with open(name,mode,encoding) as f:
来做,其中name
:是打开的文件名的字符串(可以包含文件所在的具体路径),mode
:设置打开文件的模式(访问模式):只读、写入、追加等,encoding
:编码格式(推荐使用UTF-8)。
本次案例对于小白来说是一个非常不错的练手案例,其实代码和逻辑并不复杂,但是需要一定的耐心,如果对你有用的话给小编点个赞把~~~~