DrissionPage多线程实践

DrissionPage多线程实践

背景:项目中需要抓取部分平台的数据,因为涉及到登录,且暂未实现接口登录。所以采用selenium登录后获取cookie传给requests的方式来实现。后了解到DrissionPage国产开源库,等于是把seleniumrequests结合起来(bushi,实际并不是selenium而是这位大佬自己通过websorket发送cdp命令驱动浏览器)。看了下文档很快就上手了,实现了项目需求。过程中想要提高效率,就想到了多线程驱动。

需求

设定一个本次的需求:通过纯driver的方式抓取gitee开源项目https://gitee.com/explore/all的项目简介。(gitee的项目数据是可以通过requests获取的)

获取项目信息

先把待抓取的项目名称和url获取下来

from DrissionPage import ChromiumPage, ChromiumOptions

# 获取前2页的项目链接
all_proj_url = []
co = ChromiumOptions(read_file=False)
cp = ChromiumPage(addr_or_opts=co)
for i in range(1, 3):
    cp.get(f'https://gitee.com/explore/all?page={i}')
    proj_li = cp.s_eles('c:h3>a')
    for proj in proj_li:
        url_suffix = proj.attr('href')
        title = proj.attr('title')
        all_proj_url.append(f'{title} | {url_suffix}\n')
with open('target.txt', 'w', encoding='utf8') as f_w:
    f_w.writelines(all_proj_url)

保存数据的结果如下:

image-20240131104435772

for循环

先来看下直接for循环采集简介耗时多少

import time
from DrissionPage import ChromiumPage, ChromiumOptions

co = ChromiumOptions(read_file=False)
co.set_browser_path(r"D:\ProgramData\Twinkstar\twinkstar.exe")
cp = ChromiumPage(addr_or_opts=co)



def fun(url, index):
    """
    根据项目url访问项目详情获取简介信息
    """
    cp.get(url)
    cp.wait.doc_loaded()
    content = '暂无简介'
    content_ele = cp.ele('.git-project-desc-text')
    if content_ele:
        content = content_ele.text
    # else:
    #     Log.warning(f'index:{index}  未找到元素:{content_ele.args}')
    Log.info(f'index:{index}  url: {url}  content: {content}')
    return content


st = time.time()
futures = []
with open('target.txt', 'r', encoding='utf8') as f_r:
    for index, line in enumerate(f_r):
        url = line.strip('\n').split(' | ')[1]
        fun(url, index)
cp.quit()
Log.info(f'total cost {time.time() - st}')

执行结果

D:\ProgramData\virtualenvs\all_tool\Scripts\python.exe D:\01work\02project\py_project\all_tool\Test\dp_learn.py 
MindSpore/RingMo-Framework 中科院空天信息创新研究院与华为大模型研发团队联合打造的一款用于视觉领域的全国产化自监督预训练开发套件

...

total cost 128.63436770439148

可以看到耗时有128秒。

多线程1

修改代码为多线程执行,注意,这里是有坑的!这种方法看看就行

import time
from DrissionPage import ChromiumPage, ChromiumOptions
from concurrent.futures import ThreadPoolExecutor, as_completed
from common.operate_log import Log

co = ChromiumOptions(read_file=False)
co.set_browser_path(r"D:\ProgramData\Twinkstar\twinkstar.exe")
cp = ChromiumPage(addr_or_opts=co)    # 注意这里有坑,在下面的执行结果中会体现


def fun(url):
    """
    根据项目url访问项目详情获取简介信息
    """
    cp.get(url)  # 注意这里有坑,在下面的执行结果中会体现
    content = '暂无简介'
    content_ele = cp.ele('.git-project-desc-text')
    if content_ele:
        content = content_ele.text
    else:
        Log.info('未找到元素:', content_ele.args)

    Log.info(content)
    return content


st = time.time()
with ThreadPoolExecutor(max_workers=3,thread_name_prefix='thread') as tp:
    with open('target.txt', 'r', encoding='utf8') as f_r:
        futures = [tp.submit(fun, line.strip('\n').split(' | ')[1]) for line in f_r]
        # 通过as_completed获取已完成的任务结果
        for future in as_completed(futures):
            res = future.result()
Log.info(f'total cost {time.time() - st}')

执行结果:

D:\ProgramData\virtualenvs\all_tool\Scripts\python.exe D:\01work\02project\py_project\all_tool\Test\dp_learn.py 
2024-01-31 11:34:39.992   | thread_2 | INFO     | - 用Rust实现仿nginx,力争实现一个可替代方案,http/https代理, socks5代理, 负载均衡, 反向代理, 静态文件服务器,四层TCP/UDP转发,websocket转发, 内网穿透nat
2024-01-31 11:34:41.827   | thread_1 | INFO     | - MindSpore Pandas is a data analysis framework, which is compatible with Pandas interfaces and provides distributed processing capabilities.
2024-01-31 11:34:56.413   | thread_1 | INFO     | - 👚 基于 Ant Design 设计语言的 Winform 界面库
2024-01-31 11:34:56.417   | thread_0 | INFO     | - 👚 基于 Ant Design 设计语言的 Winform 界面库
2024-01-31 11:34:59.950   | thread_0 | INFO     | - 中文对话0.2B小模型(ChatLM-Chinese-0.2B),开源所有数据集来源、数据清洗、tokenizer训练、模型预训练、SFT指令微调、RLHF优化等流程的全部代码。
2024-01-31 11:34:59.955   | thread_1 | INFO     | - 中文对话0.2B小模型(ChatLM-Chinese-0.2B),开源所有数据集来源、数据清洗、tokenizer训练、模型预训练、SFT指令微调、RLHF优化等流程的全部代码。

...

个人计划安排保存在自己的服务器中,并在任意设备之间实时同步。同时还是一个个人博客。
2024-01-31 11:35:31.259   | thread_1 | INFO     | - 本项目发布了Phytium系列CPU的Standalone BSP源码,Baremetal参考例程及其配置构建工具
2024-01-31 11:35:42.809   | thread_2 | INFO     | - 未找到元素:
2024-01-31 11:35:42.810   | thread_2 | INFO     | - 暂无简介
2024-01-31 11:35:42.811   | MainThread | INFO     | - total cost 65.02382159233093

Process finished with exit code 0

从日志中看到如下几个问题:

  1. 不同的项目简介的内容相同
  2. 页面存在找不到元素的情况

原因分析:

dp是通过websorket的方式驱动浏览器的,每次访问页面、定位元素、操作元素等均是通过一个确定的ip:port来发送的请求。

如果这个ws连接已经存在,即复用

image-20240201170612044

所以会出现页面在操作上一个请求时,当前请求发起定位元素(获取元素属性)的请求,就会存在获取的结果乱了(或者上面出现的内容重复了)。同时,如果页面对象在定位元素准备操作元素时,如果此时另一个请求发过来页面更新了(访问了新的内容)当前要操作的元素不再存在于前一个获取到的页面,就会抛出异常。

下面我们修改下代码:

import time
from DrissionPage import ChromiumPage, ChromiumOptions
from concurrent.futures import ThreadPoolExecutor
from common.operate_log import Log


def fun(url, index):
    """
    根据项目url访问项目详情获取简介信息
    """
    # 每个线程单独创建自己的cp对象即使用单独的ws通信
    co = ChromiumOptions(read_file=False)
    co.set_browser_path(r"D:\ProgramData\Twinkstar\twinkstar.exe")
    co.auto_port()  # 每个线程单独使用一个ip:port通信
    cp = ChromiumPage(addr_or_opts=co)
    cp.get(url)
    cp.wait.doc_loaded()
    content = '暂无简介'
    content_ele = cp.ele('.git-project-desc-text')
    if content_ele:
        content = content_ele.text
    Log.info(f'index:{index}  url: {url}  content: {content}')
    cp.quit(force=True)
    return content


st = time.time()
with ThreadPoolExecutor(max_workers=3, thread_name_prefix='thread') as tp:
    with open('target.txt', 'r', encoding='utf8') as f_r:
        futures = [tp.submit(fun, line.strip('\n').split(' | ')[1], index) for index, line in enumerate(f_r)]
Log.info(f'total cost {time.time() - st}')

执行结果

D:\ProgramData\virtualenvs\all_tool\Scripts\python.exe D:\01work\02project\py_project\all_tool\Test\dp_learn.py 
2024-02-01 10:11:27.391   | thread_2 | INFO     | - index:2  url: https://gitee.com/tickbh/wmproxy  content: 用Rust实现仿nginx,力争实现一个可替代方案,http/https代理, socks5代理, 负载均衡, 反向代理, 静态文件服务器,四层TCP/UDP转发,websocket转发, 内网穿透nat
2024-02-01 10:11:28.080   | thread_0 | INFO     | - index:0  url: https://gitee.com/mindspore/ringmo-framework  content: 中科院空天信息创新研究院与华为大模型研发团队联合打造的一款用于视觉领域的全国产化自监督预训练开发套件
2024-02-01 10:11:29.054   | thread_1 | INFO     | - index:1  url: https://gitee.com/mindspore/mindpandas  content: MindSpore Pandas is a data analysis framework, which is compatible with Pandas interfaces and provides distributed processing capabilities.

...

2024-02-01 10:14:07.356   | thread_2 | INFO     | - index:25  url: https://gitee.com/yeytytytytyytyt/air-drop-plus  content: 使用 Python 和快捷指令实现的 Windows 和 iOS 设备之间互传文件和剪贴板同步
2024-02-01 10:14:08.360   | thread_1 | INFO     | - index:26  url: https://gitee.com/oi-contrib/VISLite  content: 🎃 灵活、快速、简单的数据可视化交互式跨端前端库 💯
2024-02-01 10:14:17.849   | thread_0 | INFO     | - index:27  url: https://gitee.com/phytium_embedded/phytium-standalone-sdk  content: 本项目发布了Phytium系列CPU的Standalone BSP源码,Baremetal参考例程及其配置构建工具
2024-02-01 10:14:26.843   | thread_1 | INFO     | - index:28  url: https://gitee.com/blossom-editor/blossom  content: 一个支持私有部署的云端存储双链笔记软件,你可以将你所有的笔记,图片,个人计划安排保存在自己的服务器中,并在任意设备之间实时同步。同时还是一个个人博客。
2024-02-01 10:14:41.634   | thread_2 | INFO     | - index:29  url: https://gitee.com/TencentOS/TencentOS-tiny  content: 暂无简介
2024-02-01 10:14:44.896   | MainThread | INFO     | - total cost 212.73357844352722

Process finished with exit code 0

可以看到上面的俩问题解决了:1. 多个线程的返回结果不同了;2. 页面找不到元素(丢失了页面对象)的情况不存在了

但是出现了新的问题:多线程执行的时间居然比for循环耗时更长,这里用了212秒!

下面修改程序解决这个问题。

多线程2

考虑到每个线程都会单独启动cp对象,这里耗时应该是比较久的,所以尝试使用固定的cp对象,不同的线程接管不同的对象执行任务。

  • 方法1:在fun中定义启动cp对象
import time
from DrissionPage import ChromiumPage, ChromiumOptions
from concurrent.futures import ThreadPoolExecutor
from common.operate_log import Log


def fun(url, index):
    """
    根据项目url访问项目详情获取简介信息
    """
    co = ChromiumOptions(read_file=False)
    co.set_browser_path(r"D:\ProgramData\Twinkstar\twinkstar.exe")
    co.set_local_port(922 + (index % thread_num))  # 通过线程和任务的index确定使用哪个端口的cp对象,,,指定固定的端口通讯
    cp = ChromiumPage(addr_or_opts=co)  # 这里如果发现端口存在就不会再起新的浏览器
    cp.get(url)
    cp.wait.doc_loaded()
    content = '暂无简介'
    content_ele = cp.ele('.git-project-desc-text')
    if content_ele:
        content = content_ele.text
    Log.info(f'index:{index}  url: {url}  content: {content}')
    # cp.quit(force=True)  # 因为复用了cp对象,所以执行完浏览器就不要关了,不然还会重新启动
    return content


st = time.time()
thread_num = 3  # 定义了线程数
with ThreadPoolExecutor(max_workers=thread_num, thread_name_prefix='thread') as tp:
    with open('target.txt', 'r', encoding='utf8') as f_r:
        futures = [tp.submit(fun, line.strip('\n').split(' | ')[1], index) for index, line in enumerate(f_r)]
Log.info(f'total cost {time.time() - st}')

  • 方法2:启动前就把浏览器对象初始化好
import time
from DrissionPage import ChromiumPage, ChromiumOptions
from concurrent.futures import ThreadPoolExecutor
from common.operate_log import Log


def fun(url, index):
    """
    根据项目url访问项目详情获取简介信息
    """
    cp = cp_li[index % thread_num]
    cp.get(url)
    cp.wait.doc_loaded()
    content = '暂无简介'
    content_ele = cp.ele('.git-project-desc-text')
    if content_ele:
        content = content_ele.text
    Log.info(f'index:{index}  url: {url}  content: {content}')
    return content


st = time.time()
thread_num = 3
cp_li = []
for port in range(thread_num):  # 外部直接启动三个浏览器对象
    co = ChromiumOptions(read_file=False)
    co.set_browser_path(r"D:\ProgramData\Twinkstar\twinkstar.exe")
    co.set_local_port(922 + port)  # 指定固定的端口通讯
    cp_li.append(ChromiumPage(addr_or_opts=co))

with ThreadPoolExecutor(max_workers=thread_num, thread_name_prefix='thread') as tp:
    with open('target.txt', 'r', encoding='utf8') as f_r:
        futures = [tp.submit(fun, line.strip('\n').split(' | ')[1], index) for index, line in enumerate(f_r)]
for cp in cp_li:
    cp.quit()
Log.info(f'total cost {time.time() - st}')

方法2的好处是能够在执行完成后把浏览器关掉,而方法1,不太好处理。

执行结果

D:\ProgramData\virtualenvs\all_tool\Scripts\python.exe D:\01work\02project\py_project\all_tool\Test\dp_learn.py 
2024-02-01 10:31:18.986   | thread_2 | INFO     | - index:2  url: https://gitee.com/tickbh/wmproxy  content: 用Rust实现仿nginx,力争实现一个可替代方案,http/https代理, socks5代理, 负载均衡, 反向代理, 静态文件服务器,四层TCP/UDP转发,websocket转发, 内网穿透nat
2024-02-01 10:31:19.415   | thread_1 | INFO     | - index:1  url: https://gitee.com/mindspore/mindpandas  content: MindSpore Pandas is a data analysis framework, which is compatible with Pandas interfaces and provides distributed processing capabilities.
2024-02-01 10:31:37.022   | thread_0 | INFO     | - index:13  url: https://gitee.com/tsbrowser/xiangtian-workbench  content: 一款集桌面管理、效率办公、游戏娱乐为一体的副屏“桌面系统”。 除副屏以外,主屏窗口化也可以正常使用。 基于Electron和Vue全家桶开发。 想天工作台客户端的开源项目库,可编译为客户端或者web网页(web版暂时体验糟糕,不推荐)。 如果不会开发的,也可以直接下载官网版本,安装在电脑上就可以使用。
2024-02-01 10:31:38.817   | thread_1 | INFO     | - index:15  url: https://gitee.com/zhiming999/rpcf  content: 这是一个基于C++的多语言,高可用,高并发,高安全的RPC和流服务框架。自带接口定义语言,快速生成分布式应用框架,有效节省学习和开发时间。本项目持续更新中。

...

2024-02-01 10:31:53.407   | thread_1 | INFO     | - index:23  url: https://gitee.com/secretflow/secretpad  content: 🎃 灵活、快速、简单的数据可视化交互式跨端前端库 💯
2024-02-01 10:31:55.329   | thread_2 | INFO     | - index:28  url: https://gitee.com/blossom-editor/blossom  content: 一个支持私有部署的云端存储双链笔记软件,你可以将你所有的笔记,图片,个人计划安排保存在自己的服务器中,并在任意设备之间实时同步。同时还是一个个人博客。
2024-02-01 10:31:55.738   | thread_0 | INFO     | - index:27  url: https://gitee.com/phytium_embedded/phytium-standalone-sdk  content: 本项目发布了Phytium系列CPU的Standalone BSP源码,Baremetal参考例程及其配置构建工具
2024-02-01 10:32:12.030   | thread_1 | INFO     | - index:29  url: https://gitee.com/TencentOS/TencentOS-tiny  content: 暂无简介
2024-02-01 10:32:12.031   | MainThread | INFO     | - total cost 62.62155222892761

Process finished with exit code 0

通过执行日志可以看到前面提到的问题解决了,多线程执行后总耗时也降下来了,此处为62秒。

还有其他的方案吗?当然!

多线程3

前面是采用多个线程分别操作对应的浏览器对象。新的解决思路是,只启动一个浏览器对象,不同的线程操作不同的浏览器tab页。

import time
from DrissionPage import ChromiumPage, ChromiumOptions
from concurrent.futures import ThreadPoolExecutor
from common.operate_log import Log


def fun(url, index):
    """
    根据项目url访问项目详情获取简介信息
    """
    tabs = cp.tabs  # 获取所有的tab页
    if len(tabs) < thread_num:  # 没到线程数量的tab页的话就新开tab页访问
        cp.new_tab(url)
        tab = cp.latest_tab
    else:
        tab = cp.get_tab(tabs[index % thread_num])  # 取对应的tab来访问
        tab.get(url)
    tab.wait.doc_loaded()
    content = '暂无简介'
    content_ele = tab.ele('.git-project-desc-text')  # 注意这里要使用具体的tab来定位元素,而非cp对象
    if content_ele:
        content = content_ele.text
    Log.info(f'index:{index}  url: {url}  content: {content}')
    return content


st = time.time()
thread_num = 3
co = ChromiumOptions(read_file=False)
co.set_browser_path(r"D:\ProgramData\Twinkstar\twinkstar.exe")
cp = ChromiumPage(addr_or_opts=co)

with ThreadPoolExecutor(max_workers=thread_num, thread_name_prefix='thread') as tp:
    with open('target.txt', 'r', encoding='utf8') as f_r:
        futures = [tp.submit(fun, line.strip('\n').split(' | ')[1], index) for index, line in enumerate(f_r)]

cp.quit()
Log.info(f'total cost {time.time() - st}')


查看执行结果

D:\ProgramData\virtualenvs\all_tool\Scripts\python.exe D:\01work\02project\py_project\all_tool\Test\dp_learn.py 
2024-02-01 16:56:00.731   | thread_1 | INFO     | - index:3  url: https://gitee.com/aizuda/easy-retry  content: 🔥🔥🔥 灵活,可靠和快速的分布式任务重试和分布式任务调度平台
2024-02-01 16:56:00.732   | thread_0 | INFO     | - index:4  url: https://gitee.com/jeremyczhen/fdbus  content: Fast Distributed Bus (FDBus)
2024-02-01 16:56:01.859   | thread_2 | INFO     | - index:5  url: https://gitee.com/FredyXu/coffee  content: 一个基于ESP32使用ESP-IDF开发的摩尔斯电码练习器。

...

2024-02-01 16:56:20.427   | thread_0 | INFO     | - index:28  url: https://gitee.com/blossom-editor/blossom  content: 一个支持私有部署的云端存储双链笔记软件,你可以将你所有的笔记,图片,个人计划安排保存在自己的服务器中,并在任意设备之间实时同步。同时还是一个个人博客。
2024-02-01 16:56:31.682   | thread_1 | INFO     | - index:29  url: https://gitee.com/TencentOS/TencentOS-tiny  content: 暂无简介
2024-02-01 16:56:37.540   | MainThread | INFO     | - total cost 44.64077115058899

Process finished with exit code 0

通过执行日志开到,效率大幅度提升。总耗时现在降到了44秒。

总结

至此,多线程操作cp就验证完毕了。可以看到有三种思路:

  1. 每个线程分别启动一个浏览器去执行任务,通过auto_port实现 – 每次启动关闭,会增加耗时
  2. 根据线程数提前启动浏览器进程后,在fun中使用不同浏览器执行,最后关闭进程。同时也介绍了set_local_port方法,本质和上面的auto_port一样,一个指定,一个随机
  3. 使用一个浏览器,启动多个tab页,多个线程分别操作不同的tab页即可。这里用到的是new_tabget_tab等方法

补充,本次验证使用的解释器和库版本:

Python        3.9.5
DrissionPage        4.0.4.3

由于dp库的研发大佬还在不断迭代,后面可能有更方便的方法。后期项目中如有用到,再更新新版本实现方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

部署YUM仓库服务

一、yum仓库 1. yum简介 yum是一个基于RPM包&#xff08;是Red-Hat Package Manager红帽软件包管理器的缩写&#xff09;构建的软件更新机制&#xff0c;能够自动解决软件包之间的依赖关系。 为什么会有依赖关系的发生 因为linux本身就是以系统简洁为自身优势&#xff0c;所以…

大数据信用报告应该去哪里查询比较好呢?

对于个人而言&#xff0c;大数据信用报告也变得越来越重要。那么&#xff0c;大数据信用报告应该去哪里查询呢?本文将为您详细介绍征信和大数据的区别&#xff0c;并推荐一个可靠的大数据平台。 首先&#xff0c;我们需要了解征信和大数据的区别 征信报告 依法采集、整理、保存…

day13_oop_抽象类_接口

今日内容 零、 复习昨日 一、作业 二、抽象 三、接口 零、 复习昨日 final的作用 最终的,修饰的类,属性,方法不能改变类不能继承,属性不能改变(常量),方法不能重写 static修饰方法的特点 修饰的属性方法在内存只有一份随着类加载而初始化不要new,可以通过类名直接调用被该类的所…

异或运算实现加密解密

异或运算符^&#xff0c;相同为0&#xff0c;不同为1&#xff08;同0非1&#xff09; 由异或运算法则可知&#xff1a;a ^ a 0&#xff0c;a ^ 0 a 如果c a ^ b&#xff0c;那么a b ^ c&#xff0c;即a ^ b ^ b a&#xff0c;^ 的逆运算仍然是 ^ 利用异或运算的性质&am…

LabVIEW船舶自动识别系统

在现代航海领域&#xff0c;安全高效的船舶自动识别系统对于保障航行安全和提高船舶管理效率非常重要。介绍了利用LabVIEW软件开发的一个船舶自动识别系统&#xff0c;该系统通过先进的数据采集和信号处理技术&#xff0c;显著提升了传统自动识别系统的性能。 这个船舶自动识别…

IAR编译和调试CMS32L051

0 Preface/Foreword 0.1 参考文档 中微半导体BAT系列单片机学习笔记_V1.1.pdf 1 配置方法 1.1 编译工具链添加 CMS对于IAR工具&#xff0c;有一个插件文件&#xff0c;用于安装对应的CMS系列芯片。 工具名称&#xff1a;iar_plug20210926.7z 按照完成之后&#xff0c;可…

电脑监控软件都有哪些?哪款好用?

在当今数字化时代&#xff0c;电脑监控软件已经成为企业和个人用户保障信息安全、管理电脑资源的重要工具。市场上存在多种电脑监控软件&#xff0c;每款软件都有其独特的优点和适用场景。本文将为您介绍几款常见的电脑监控软件&#xff0c;并分析哪款更适合您的需求。 绿虫电…

【windows计算md5】

windows计算md5 使用Windows系统自带的certutil命令。在命令提示符中输入“certutil -hashfile 文件路径 MD5”即可计算文件的MD5值。 例如&#xff0c;要计算D盘根目录下的test.txt文件的MD5值&#xff0c; 可以输入certutil -hashfile D:\test.txt MD5

接口测试(jmeter和postman 接口使用)

接口测试基础知识 接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。把前端&#xff08;client&#xff09;和后端&#xff08;server&#xff09;联系起来&#xff0c;测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系…

7.2、子集求和问题与背包密码系统

7.2、子集求和问题与背包密码系统 一、数学描述 1.1、第一种描述 20 世纪 70 年代末&#xff0c;默克尔和赫尔曼首次尝试将密码系统建立在一个 NP-完全问题上。他们使用了以下数学问题的一个版本&#xff0c;该问题是对经典knapsack问题的概括。 子集和问题 假设你有一个正…

MySQL索引的原理和SQL优化策略

1. 索引 在InnoDB存储引擎中&#xff0c;索引分为聚簇索引和辅助索引两种类型。 聚簇索引是指基于表的主键构建的索引&#xff0c;它决定了表中数据的物理存储顺序。也就是说&#xff0c;聚簇索引中的键值按照主键的顺序来排序&#xff0c;并且每个叶子节点存储的是整个表行的…

关于数组中相邻元素的合并处理方法总结

这两天在开发需求的过程中&#xff0c;需要处理一个数组数据&#xff0c;对相邻元素的具有相同的点合并元素里面的数组。 我们先看一下处理的数组是什么样的。 let array [{name: 数据来源1,list: [{dataCode: 12,dataName: 照片,dataSort: 20,fileList: []}]}, {name: 数据来…

React18-模拟列表数据实现基础表格功能

文章目录 分页功能分页组件有两种接口参数分页类型用户列表参数类型 模拟列表数据分页触发方式实现目录 分页功能 分页组件有两种 table组件自带分页 <TableborderedrowKey"userId"rowSelection{{ type: checkbox }}pagination{{position: [bottomRight],pageSi…

python 实现 macOS状态栏 网速实时显示

安装依赖包&#xff1a; pip install pillow psutil rumpsnetSpeedApp.py from PIL import Image, ImageDraw, ImageFont import psutil import rumpsclass NetSpeedApp(rumps.App):def __init__(self):super(NetSpeedApp, self).__init__("NetSpeed")self.titlese…

【javase】——类和对象

莫道桑榆晚&#xff0c;为霞尚满天。文章目录 面向对象的初步认识面向对象与面向过程什么是面向对象 类的定义和使用类的定义格式 类的实例化什么是实例化类和对象的使用 this引用什么是this引用this 引用的特性。 对象的构造以及初始化如何初始化对象构造方法首先第一&#xf…

喝汽水问题

答案&#xff1a; #include <stdio.h> int main() {int num 0; //可以喝汽水的次数int mon 20; //钱int cup 0; //瓶子数for (mon 20; mon > 0; mon--) //每次花1元钱买汽水喝{num; //可以喝汽水的次数加1cup; //瓶子数加1if (cup 2) //如果瓶子…

Common Mistakes in German

Comman Mistakes in German 1, Haus oder Hause2, ja nein oder doch(1) Positive Fragen(2) Negative Fragen 1, Haus oder Hause 2, ja nein oder doch (1) Positive Fragen (2) Negative Fragen kein / nicht P3

MySQL 可重复读隔离级别,完全解决幻读了吗?

文章目录 前言一、什么是幻读&#xff1f;二、快照读是如何避免幻读的&#xff1f;三、当前读是如何避免幻读的&#xff1f;四、幻读被完全解决了吗&#xff1f;场景1场景2 总结 前言 MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」&#xff0c;但是它很大程度上避免幻读…

qt内存自动释放的两种情况

qt内存管理机制 QObject的parent 我们时常能看到QWidget或者其他的控件的构造函数中有一项参数parent&#xff0c;默认值都为NULL&#xff0c;例如&#xff1a; QLineEdit(const QString &contents, QWidget *parent nullptr); QWidget(QWidget *parent nullptr, Qt::…

新火种AI|哄哄大模型的火爆,给了普通人AI创业破局的关键

作者&#xff1a;一号 编辑&#xff1a;美美 人们似乎更喜欢把AI当做玩具&#xff0c;而非工具。 近日&#xff0c;一款名为哄哄模拟器的AI原生应用火了&#xff0c;一天之内就吸引了60万用户。 哄哄模拟器设置了多种情侣吵架场景&#xff0c;无论你是男是女&#xff0c;都…