前言
我的研究方向是图片地理定位,简而言之,就是给定一张在全球随机位置拍摄的图片,在不包含exif等额外信息的前提下,仅仅通过图像来确定这个图片的拍摄经纬度。在CVPR2024上发表了的论文《OpenStreetView-5M: The Many Roads to Global Visual Geolocation》推出了一个适用于我的领域的全新数据集,其中有在全球拍摄的图片和对应的经纬度信息,一共有五百万多张图片。我的研究需要对这个数据集进行拓展,不仅要街景图片,也要经纬度对应的卫星图片。因此我的本次作业打算写一个基于python的爬虫,通过经纬度映射到url参数 ,然后获取谷歌地图的瓦片。然而网络上的相关教程都是几年前的了,谷歌的url都换了,我就浅浅重新从0来实现这个程序,并记录一下实现过程,正好也把我的python大作业完成了。
考虑到这个数据集很大,单线程的爬虫肯定是不行的。正好python课上老师教了多线程,因此就用多线程(伪)+异步的方式来做。另外要学习经纬度到url参数的转换原理并进行实验,然后要采用代理池的方式来防止谷歌的反爬。
总结要完成的任务,就是以下几点:
- 多线程爬虫的实现
- 经纬度到瓦片参数的转换
- 卫星图片的拼接
- 任务的分配
- 数据的存储
- 反爬:使用代理池、随机请求头、线程呼吸好的,让我们开始。
多线程爬虫实现
首先,使用burpsuite抓包,获得能够成功爬取的包头
有关burp的使用,我之前也写过一篇博客,这里就不再赘述了
有了这个包,基于这个包进行单元测试,看看能不能在纯python环境中成功实现。这里我先使用了本地的clash for windows代理,然后不使用任务队列,仅运行单个任务。fetch函数存储了api,main函数运行任务队列。
import aiohttp
import asyncio
asyncdeffetch(session, x, y, z):
url =f'https://khms1.google.com/kh/v=991?x={x}&y={y}&z={z}'
headers ={'Host':'khms1.google.com','Origin':'https://www.google.com','Sec-Ch-Ua-Platform':'"Windows"','Accept-Language':'zh-CN,zh;q=0.9','Sec-Ch-Ua':'"Not?A_Brand";v="99", "Chromium";v="130"','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36','Sec-Ch-Ua-Mobile':'?0','Accept':'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8','X-Client-Data':'CJ+MywE=','Sec-Fetch-Site':'same-site','Sec-Fetch-Mode':'cors','Sec-Fetch-Dest':'image','Referer':'https://www.google.com/','Accept-Encoding':'gzip, deflate, br','Priority':'i'}# 使用本地代理
proxy ='http://127.0.0.1:7890'asyncwith session.get(url, headers=headers, proxy=proxy)as response:# 我们需要的是图片内容,所以直接读取内容
content =await response.read()return content
asyncdefmain(x, y, z):# 使用aiohttp的ClientSessionasyncwith aiohttp.ClientSession()as session:
content =await fetch(session, x, y, z)withopen(f'image_x{x}_y{y}_z{z}.png','wb')as f:
f.write(content)print(f'图片已保存:image_x{x}_y{y}_z{z}.png')# 传入参数x, y, z
x =0
y =0
z =0# 运行异步爬虫
loop = asyncio.get_event_loop()
loop.run_until_complete(main(x, y, z))
爬到的图片就是下面这个玩意,一张大小为256x256的png图片。谷歌地图当下的api为 https://khms1.google.com/kh/v=991?x={x}&y={y}&z={z},其中x,y是区域坐标,z是缩放大小。我们使用的测试参数为0,0,0,缩放大小为0,谷歌返回了整个地球的照片,图片的中点为经纬度为0,0的点,非洲西部的赤道几内亚湾。当缩放等级为0的时候,x,y都只有0。
当我们将缩放设置成1时,x和y可以是(0,0)(1,0)(0,1)(1,1)四个点。其中中点坐标就是偏移一半经纬度后的点,比如当参数为x=0,y=0,z=1时,得到的图片中心经纬度为(-90,45)。
显然,z越大,图片就越清晰,道理很简单。下面要解决的问题是经纬度如何映射到x,y,z上,以及如何确定一组参数,获得各个缩放等级的图像,以综合更多维度的地理信息。
经纬度到瓦片参数的转换
谷歌地图瓦片系统是一个四叉树结构,其中每个瓦片都有一个唯一的 x, y, z 坐标。以下是转换过程的步骤:
- 确定缩放级别 z: 谷歌地图的缩放级别 z 范围是从 0 到 21(或更高,但通常最高到 21)。每个缩放级别将地球表面分成 2^z * 2^z 个瓦片。
- 将经纬度转换为瓦片坐标: - 将经度转换为 x 坐标: - 地球可以看作是 -180 到 180 经度的范围。- 将经度范围 [-180, 180] 映射到 [0, 2^z - 1]。- 将纬度转换为 y 坐标: - 地球可以看作是 -85.0511 到 85.0511 纬度的范围(这是谷歌地图可以显示的最大纬度范围)。- 将纬度范围 [-85.0511, 85.0511] 映射到 [0, 2^z - 1]。
数学解释:
经度转换为 x 坐标: (lon + 180.0) / 360.0 将经度从 [-180, 180] 范围映射到 [0, 1]。 * (1 << zoom) 将这个比例乘以 2^zoom,得到最终的瓦片 x 坐标。
纬度转换为 y 坐标: math.log(math.tan(lat * math.pi / 180.0) + 1.0 / math.cos(lat * math.pi / 180.0)) / math.pi 是一个将纬度从 [-π/2, π/2] 范围映射到 [0, 1] 的函数。 (1.0 - …) 反转这个比例,因为瓦片坐标的 y 轴是从上到下增长的,而纬度是从下到上增长的。 * (1 << zoom) 同样地,将这个比例乘以 2^zoom,得到最终的瓦片 y 坐标。
import math
deflatlon_to_tile(lat, lon, zoom):"""
将经纬度转换为谷歌地图瓦片坐标。
参数:
lat -- 纬度
lon -- 经度
zoom -- 缩放级别
返回:
(x, y) -- 瓦片坐标
"""# 确保纬度在谷歌地图的显示范围内
lat =max(min(lat,85.0511),-85.0511)# 计算瓦片坐标
x =int((lon +180.0)/360.0*(1<< zoom))
y =int((1.0- math.log(math.tan(lat * math.pi /180.0)+1.0/ math.cos(lat * math.pi /180.0))/ math.pi)/2.0*(1<< zoom))return x, y
# 示例使用
zoom_level =1
latitude =0
longitude =0
tile_x, tile_y = latlon_to_tile(latitude, longitude, zoom_level)print(f"Tile coordinates at zoom level {zoom_level} for latitude {latitude} and longitude {longitude}: x={tile_x}, y={tile_y}")
下面我们测试一下这个代码能不能正常运行。我选取了西安青少年宫的坐标(34.265056615386364, 108.95181859260963),设置缩放等级为19,最终生成的瓦片参数为x=420816, y=208970,放到之前的脚本中,得到的图片如下:
我们再去谷歌地图上看一下,可以看到那个很有特点的建筑,确实是成功地把对应的图片获取到了。
卫星图片的拼接
已经获得了图片,但是我们的目标是获得尽可能清晰的卫星图。因此我们实现再实现一个小任务,将四个高级缩放的图片拼接成一个低级缩放的图片。来看看获得的图片和上一级相比是否更清晰,以确定最优的爬取缩放等级,来节省资源。
我们定义两个新函数,latlon_to_tile_bigger用于生成临近的四张图片的参数,combine_tiles用于拼接这四张图片。
deflatlon_to_tile_bigger(lat, lon, zoom):"""
获取所给经纬度周围的四个瓦片图片的坐标进行拼接
参数:
lat -- 纬度
lon -- 经度
zoom -- 缩放级别
返回:
(x, y) -- 瓦片坐标
"""
zoom = zoom
# 确保纬度在谷歌地图的显示范围内
lat =max(min(lat,85.0511),-85.0511)# 计算瓦片坐标
x =int((lon +180.0)/360.0*(1<< zoom))
y =int((1.0- math.log(math.tan(lat * math.pi /180.0)+1.0/ math.cos(lat * math.pi /180.0))/ math.pi)/2.0*(1<< zoom))return[(x, y+1),(x-1, y+1),(x, y),(x-1, y)]from PIL import Image
defcombine_tiles(tile_images):"""
将四个瓦片图片拼接成一张大图。
参数:
tile_images -- 包含四个 Image 对象的列表,分别是 (x, y), (x-1, y), (x, y-1), (x-1, y-1) 的瓦片图片。
返回:
Image -- 拼接后的图片。
"""# 确保提供了四张图片iflen(tile_images)!=4:raise ValueError("需要四张图片来拼接")for i,byte_data inenumerate(tile_images):# 使用io.BytesIO创建一个字节流
image_stream = io.BytesIO(byte_data)# 使用Image.open从字节流中打开图像
tile_images[i]= Image.open(image_stream)# 假设所有图片尺寸相同
width, height = tile_images[0].size
# 创建一个新的空白图片,大小是单张图片的两倍
combined_image = Image.new('RGB',(width *2, height *2))# 将四张图片放置到正确的位置
combined_image.paste(tile_images[2],(width,0))# (x, y)
combined_image.paste(tile_images[3],(0,0))# (x-1, y)
combined_image.paste(tile_images[0],(width, height))# (x, y-1)
combined_image.paste(tile_images[1],(0, height))# (x-1, y-1)return combined_image
然后修改主函数,使其能一次运行四个爬取任务,随后用上面的函数存为一个大图:
asyncdefmain_multitask(lat, lon, z):# 使用aiohttp的ClientSessionasyncwith aiohttp.ClientSession()as session:
parse = latlon_to_tile_bigger(lat,lon,z)
tasks =[fetch(session,i[0],i[1],z)for i in parse]
results =await asyncio.gather(*tasks)return results
z =20
result = asyncio.run(main_multitask(lat,lon,z))
bigger_image = combine_tiles(result)print(bigger_image.size)
bigger_image.save("bigger_image.png")
最终我们用缩放等级为20,爬取四张图进行拼接后得到的大图如下。
和上面那个图对比分析,可以看到明显要比zoom=19的那张清晰一点。但是两张图片很明显是同一张图,猜想谷歌地图的低缩放tile就是高缩放tile大图的压缩版本。
因此我们想要尽可能获得更清晰的图片的话,就要用最小的tile来拼接。已知,谷歌地图的最大缩放等级为21,我们用z=21,生成16张图进行拼接。但是尝试后发现没啥变化。因此,四张缩放等级为20的图片拼接后能尽可能避免图片压缩,同时,可以尽量获得周围的环境信息。
到这里,我们的程序已经很出色了。提取几个osv5m中的样本进行测试,获取到图片的中心正是照片的拍摄位置~
然而,这里有个比较坑的地方。如果减小缩放等级数值,会发现低级缩放图片并不位于图片正中。为了确保低级图片在正中,一个可行的解法是获取高两级的缩放图片,然后爬取该图片上下左右以及四个角位置的共八张图片后进行拼接。虽然需要爬取的图片数量变多了,但是这样能保证下级图片处在图片中心五分之一的位置,这是我需要的。
任务分配与数据存储
一张图片分别存为jpg和png,大小差了九倍。果断采用jpg进行读取。
我们想要获取各个缩放等级的,包含不同范围内特征的图片,需要从大缩放等级拓展到小缩放等级。对于每一个经纬度,我们获取z=10-19的所有图片,观察哪些是需要爬取的。
- z=19-17:街道、街区级
- z=16-14:城市、轮廓级
- z=13-10,地貌级,看不出城市细节
关于怎么选择缩放大小,还需要做实验来判断。目前从19开始间隔选择,选择4\7\10\13\16\19这几个缩放级别。当前数据集大小为200G左右,在数据扩展后,量级大概会来到1.4T。这个数据量倒也能够接收,不过得买一块新硬盘了,还得再买一个新vpn。
数据的存储就很简单了,每一个缩放等级新建一个文件夹,按照原文件名存就好。
这样,每张图片有6个缩放等级,每个缩放等级请求九次。每张图进行54个请求,一次异步请求n张图片,来组成一个异步队列。
asyncdefrun_5X5(session,lat, lon, z,id):# 使用aiohttp的ClientSessionprint(f"执行:id={id}.z={z}")
parse = latlon_to_tile_bigger5X5(lat,lon,z+2)
tasks =[fetch(session,i[0],i[1],z+2)for i in parse]
results =await asyncio.gather(*tasks)try:
bigger_image = combine_tiles5X5(byte2img(results))
bigger_image.save(f"test/{z}/{id}.jpg")except:print(results[:10],lat,lon,z,id)asyncdefrun_queqe(data):asyncwith aiohttp.ClientSession()as session:
tasks =[run_5X5(session,i[1],i[2],z,i[0])for i in data for z inrange(4,20,3)]await asyncio.gather(*tasks)
data =[[1,53.32936106,44.832726],[2,40.453126,-119.334174],[3,60.64325302,9.051189134],[4,17.1758181,77.88178872],[5,32.02595871,-93.53468712],[6,-23.97027904,-53.6356253],]
asyncio.run(run_queqe(data))
结果:
感觉不错,然后就是反反爬了。
反反爬
使用队列呼吸
在组织任务的时候,每次发多少并发请求是有讲究的。长期维持高并发会被识别出来,太低的并发根本短时间里又完成不了我们五百万级的任务。在处理这类问题时,常采用短时间维持高并发,长时间维持中低并发的方式,在模拟用户操作的同时,防止被服务器封禁。
我们以10000张图片为一个循环,前1000条使用20个请求并发,然后2000条使用40个请求并发,中间4000条使用50个请求并发,最后3000条使用20个请求并发,然后随机休眠3-5分钟。全都运行完之后应该会报一个索引不存在的错误(数据量不能被10000整除)。到时候对剩下的图片进行统一爬取即可。
from tqdm import tqdm
import pandas as pd
import time
import random
df = pd.read_csv("test.csv")
value = df[['id','latitude','longitude']].values.tolist()del df
for i inrange(len(value)//10000):
datatemp = value[i*10000:i*10000+1000]print(f"第{i}轮开始")
start_time = time.time()for j in tqdm(range(1000//10)):
asyncio.run(run_queqe(datatemp[j*10:j*10+10]))
datatemp = value[i*10000+1000:i*10000+3000]for j in tqdm(range(2000//20)):
asyncio.run(run_queqe(datatemp[j*20:j*20+20]))
datatemp = value[i*10000+3000:i*10000+7000]for j in tqdm(range(4000//50)):
asyncio.run(run_queqe(datatemp[j*50:j*50+50]))
datatemp = value[i*10000+7000:(i+1)*10000]for j in tqdm(range(3000//20)):
asyncio.run(run_queqe(datatemp[j*20:j*20+20]))
sleep_duration = random.randint(180,300)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"第{i}轮结束,执行时间{elapsed_time}秒,将睡眠{sleep_duration}秒")
time.sleep(sleep_duration)
使用随机代理
有很多种实现方法,我采用了比较简单的一种,修改clash for windows的yaml文件,启用负载均衡。
随机请求头
我这个项目好像不需要。
下面是所有代码:
import aiohttp
import asyncio
import math
import io
deflatlon_to_tile(lat, lon, zoom):# 确保纬度在谷歌地图的显示范围内
lat =max(min(lat,85.0511),-85.0511)# 计算瓦片坐标
x =int((lon +180.0)/360.0*(1<< zoom))
y =int((1.0- math.log(math.tan(lat * math.pi /180.0)+1.0/ math.cos(lat * math.pi /180.0))/ math.pi)/2.0*(1<< zoom))return x, y
deflatlon_to_tile_bigger(lat, lon, zoom):"""
获取所给经纬度周围的四个瓦片图片的坐标进行拼接
参数:
lat -- 纬度
lon -- 经度
zoom -- 缩放级别
返回:
(x, y) -- 瓦片坐标
"""
zoom = zoom
# 确保纬度在谷歌地图的显示范围内
lat =max(min(lat,85.0511),-85.0511)# 计算瓦片坐标
x =int((lon +180.0)/360.0*(1<< zoom))
y =int((1.0- math.log(math.tan(lat * math.pi /180.0)+1.0/ math.cos(lat * math.pi /180.0))/ math.pi)/2.0*(1<< zoom))return[(x-1,y+1),(x,y+1),(x+1,y+1),(x-1,y),(x,y),(x+1,y),(x-1,y-1),(x,y-1),(x+1,y-1)]deflatlon_to_tile_bigger5X5(lat, lon, zoom):"""
获取所给经纬度周围的四个瓦片图片的坐标进行拼接
参数:
lat -- 纬度
lon -- 经度
zoom -- 缩放级别
返回:
(x, y) -- 瓦片坐标
"""
zoom = zoom
# 确保纬度在谷歌地图的显示范围内
lat =max(min(lat,85.0511),-85.0511)# 计算瓦片坐标
x =int((lon +180.0)/360.0*(1<< zoom))
y =int((1.0- math.log(math.tan(lat * math.pi /180.0)+1.0/ math.cos(lat * math.pi /180.0))/ math.pi)/2.0*(1<< zoom))return[(x-2,y+2),(x-1,y+2),(x,y+2),(x+1,y+2),(x+2,y+2),(x-2,y+1),(x-1,y+1),(x,y+1),(x+1,y+1),(x+2,y+1),(x-2,y ),(x-1,y ),(x,y ),(x+1,y ),(x+2,y ),(x-2,y-1),(x-1,y-1),(x,y-1),(x+1,y-1),(x+2,y-1),(x-2,y-2),(x-1,y-2),(x,y-2),(x+1,y-2),(x+2,y-2)]from PIL import Image
defbyte2img(tile_images):for i,byte_data inenumerate(tile_images):# 使用io.BytesIO创建一个字节流
image_stream = io.BytesIO(byte_data)# 使用Image.open从字节流中打开图像
tile_images[i]= Image.open(image_stream)return tile_images
defcombine_tiles2x2(tile_images):"""
将四个瓦片图片拼接成一张大图。
参数:
tile_images -- 包含四个 Image 对象的列表,分别是 (x, y), (x-1, y), (x, y-1), (x-1, y-1) 的瓦片图片。
返回:
Image -- 拼接后的图片。
"""# 假设所有图片尺寸相同
width, height = tile_images[0].size
# 创建一个新的空白图片,大小是单张图片的两倍
combined_image = Image.new('RGB',(width *2, height *2))# 将四张图片放置到正确的位置
combined_image.paste(tile_images[2],(width,0))# (x, y)
combined_image.paste(tile_images[3],(0,0))# (x-1, y)
combined_image.paste(tile_images[0],(width, height))# (x, y-1)
combined_image.paste(tile_images[1],(0, height))# (x-1, y-1)return combined_image
defcombine_tiles(tile_images):"""
将九个瓦片图片拼接成一张大图。
参数:
tile_images -- 包含九个 Image 对象的列表,分别是
(x-1, y+1), (x, y+1), (x+1, y+1),
(x-1, y), (x, y), (x+1, y),
(x-1, y-1), (x, y-1), (x+1, y-1) 的瓦片图片。
返回:
Image -- 拼接后的图片。
"""# 假设所有图片尺寸相同
width, height = tile_images[4].size # 使用中心图片的尺寸# 创建一个新的空白图片,大小是单张图片的三倍
combined_image = Image.new('RGB',(width *3, height *3))# 将九张图片放置到正确的位置
combined_image.paste(tile_images[6],(0,0))# (x-1, y+1)
combined_image.paste(tile_images[7],(width,0))# (x+1, y+1)
combined_image.paste(tile_images[8],(width *2,0))# (x-1, y)
combined_image.paste(tile_images[3],(0, height))# (x+1, y)
combined_image.paste(tile_images[4],(width, height))# (x, y+1)
combined_image.paste(tile_images[5],(width*2, height))# (x, y-1)
combined_image.paste(tile_images[0],(0, height *2))# (x-1, y-1)
combined_image.paste(tile_images[1],(width , height *2))# (x+1, y-1)
combined_image.paste(tile_images[2],(width *2, height *2))# (x-1, y)return combined_image
defcombine_tiles5X5(tile_images):"""
将九个瓦片图片拼接成一张大图。
参数:
tile_images -- 包含九个 Image 对象的列表,分别是
(x-1, y+1), (x, y+1), (x+1, y+1),
(x-1, y), (x, y), (x+1, y),
(x-1, y-1), (x, y-1), (x+1, y-1) 的瓦片图片。
返回:
Image -- 拼接后的图片。
"""# 假设所有图片尺寸相同
width, height = tile_images[4].size # 使用中心图片的尺寸# 创建一个新的空白图片,大小是单张图片的三倍
combined_image = Image.new('RGB',(width *5, height *5))# 将九张图片放置到正确的位置 for i inrange(5):for j inrange(5):
combined_image.paste(tile_images[i*5+j],(width * j, height *(4-i)))return combined_image
asyncdeffetch(session, x, y, z):
url =f'https://khms1.google.com/kh/v=991?x={x}&y={y}&z={z}'
headers ={'Host':'khms1.google.com','Origin':'https://www.google.com','Sec-Ch-Ua-Platform':'"Windows"','Accept-Language':'zh-CN,zh;q=0.9','Sec-Ch-Ua':'"Not?A_Brand";v="99", "Chromium";v="130"','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36','Sec-Ch-Ua-Mobile':'?0','Accept':'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8','X-Client-Data':'CJ+MywE=','Sec-Fetch-Site':'same-site','Sec-Fetch-Mode':'cors','Sec-Fetch-Dest':'image','Referer':'https://www.google.com/','Accept-Encoding':'gzip, deflate, br','Priority':'i'}# 使用本地代理
proxy ='http://127.0.0.1:7890'asyncwith session.get(url, headers=headers, proxy=proxy)as response:# 这里假设我们需要的是图片内容,所以直接读取内容
content =await response.read()return content
asyncdefmain(lat, lon, z):# 使用aiohttp的ClientSessionasyncwith aiohttp.ClientSession()as session:
i = latlon_to_tile(lat,lon,z)
content =await fetch(session, i[0], i[1], z)# 假设我们获取的是图片,可以将其保存到本地withopen(f'image_x{i[0]}_y{i[1]}_z{z}.png','wb')as f:
f.write(content)asyncdefrun_5X5(session,lat, lon, z,id):# 使用aiohttp的ClientSessionprint(f"执行:id={id}.z={z}")
parse = latlon_to_tile_bigger5X5(lat,lon,z+2)
tasks =[fetch(session,i[0],i[1],z+2)for i in parse]
results =await asyncio.gather(*tasks)try:
bigger_image = combine_tiles5X5(byte2img(results))
bigger_image.save(f"test/{z}/{int(id)}.jpg")except:print(results[:10],lat,lon,z,id)asyncdefrun_3X3(lat, lon, z,id):# 使用aiohttp的ClientSessionasyncwith aiohttp.ClientSession()as session:
parse = latlon_to_tile_bigger(lat,lon,z+2)
tasks =[fetch(session,i[0],i[1],z+2)for i in parse]
results =await asyncio.gather(*tasks)
bigger_image = combine_tiles(byte2img(results))
bigger_image.save(f"data/{z}/{int(id)}.jpg")asyncdefrun_queqe(data):asyncwith aiohttp.ClientSession()as session:
tasks =[run_5X5(session,i[1],i[2],z,i[0])for i in data for z inrange(4,20,3)]await asyncio.gather(*tasks)# 传入参数x, y, z# lat = 17.03617016# lon = 95.55530584# z = 17# for z in range(5,21,3):# asyncio.run(run_5X5(lat,lon,z))
data =[[1,53.32936106,44.832726],[2,40.453126,-119.334174],[3,60.64325302,9.051189134],[4,17.1758181,77.88178872],[5,32.02595871,-93.53468712],[6,-23.97027904,-53.6356253],]from tqdm import tqdm
import pandas as pd
import time
import random
df = pd.read_csv("test.csv")
value = df[['id','latitude','longitude']].values.tolist()del df
for i inrange(len(value)//10000):
datatemp = value[i*10000:i*10000+1000]print(f"第{i}轮开始")
start_time = time.time()for j in tqdm(range(1000//10)):
asyncio.run(run_queqe(datatemp[j*10:j*10+10]))
datatemp = value[i*10000+1000:i*10000+3000]for j in tqdm(range(2000//20)):
asyncio.run(run_queqe(datatemp[j*20:j*20+20]))
datatemp = value[i*10000+3000:i*10000+7000]for j in tqdm(range(4000//50)):
asyncio.run(run_queqe(datatemp[j*50:j*50+50]))
datatemp = value[i*10000+7000:(i+1)*10000]for j in tqdm(range(3000//20)):
asyncio.run(run_queqe(datatemp[j*20:j*20+20]))
sleep_duration = random.randint(180,300)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"第{i}轮结束,执行时间{elapsed_time}秒,将睡眠{sleep_duration}秒")
time.sleep(sleep_duration)
版权归原作者 navigateException 所有, 如有侵权,请联系我们删除。