【Python爬虫(32)】从单飞 to 团战:Python多线程爬虫进化史

【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取,还涉及数据处理与分析。无论是新手小白还是进阶开发者,都能从中汲取知识,助力掌握爬虫核心技能,开拓技术视野。

目录

  • 一、单线程爬虫的困境
  • 二、改造之路:迈向多线程爬虫
    • 2.1 引入 threading 模块
    • 2.2 单线程爬虫代码剖析
    • 2.3 多线程爬虫的实现步骤
    • 2.4 代码示例与讲解
  • 三、应对挑战:解决资源竞争问题
    • 3.1 资源竞争的产生原因
    • 3.2 互斥锁(Mutex)的使用
    • 3.3 信号量(Semaphore)的应用
    • 3.4 队列(Queue)的运用
  • 四、数据说话:性能测试与分析
    • 4.1 测试环境与方法
    • 4.2 测试结果展示
    • 4.3 结果分析与总结
  • 五、总结与展望


一、单线程爬虫的困境

在爬虫的世界中,单线程爬虫就像是一位独自忙碌的工匠,每次只能专注于一项任务。当需要爬取大量数据时,单线程爬虫的局限性便会暴露无遗。假设我们要爬取一个包含上千个页面的网站,单线程爬虫会按照顺序,依次向每个页面发送请求,然后等待服务器响应,获取页面内容后再进行处理。在这个过程中,一旦遇到网络延迟较高或者服务器响应缓慢的情况,爬虫就会被迫等待,而这段等待的时间里,程序几乎处于闲置状态,无法进行其他工作。

以爬取电商网站的商品信息为例,单线程爬虫需要逐个访问每个商品页面,获取商品名称、价格、描述等信息。如果该电商网站有 1000 个商品页面,且每个页面的请求和处理时间平均为 1 秒,那么即使不考虑网络波动等因素,单线程爬虫完成所有页面的爬取也需要 1000 秒,即约 16.7 分钟。这显然是非常耗时的,在实际应用中,我们可能需要更快地获取数据,以满足业务需求。

此外,单线程爬虫在面对大量并发请求时,效率会更低。因为它无法充分利用计算机的多核处理器资源,只能在一个核心上依次执行任务,导致其他核心处于闲置状态,浪费了计算资源。所以,为了提高爬虫的效率,我们需要引入多线程技术,让爬虫能够同时处理多个任务,从而大大缩短数据爬取的时间。

二、改造之路:迈向多线程爬虫

2.1 引入 threading 模块

在 Python 中,threading模块是实现多线程编程的核心工具。它就像是一个经验丰富的指挥家,能够有条不紊地管理和协调各个线程的工作。threading模块对底层的线程操作进行了封装,提供了一系列简单易用的类和方法,让开发者可以轻松地创建、启动、暂停、终止线程,以及处理线程间的同步和通信问题 。例如,通过threading.Thread类,我们可以创建一个新的线程对象,并指定该线程要执行的任务函数。使用threading.Lock类可以创建锁对象,用于解决多线程访问共享资源时的数据冲突问题。threading.Event类则提供了一种线程间的通知机制,使一个线程可以通知其他线程某个事件已经发生。这些功能使得threading模块成为 Python 多线程编程中不可或缺的一部分。

2.2 单线程爬虫代码剖析

下面是一段简单的单线程爬虫代码示例,它的功能是爬取指定网页的标题:

import requests
from bs4 import BeautifulSoupdef crawl_page(url):response = requests.get(url)if response.status_code == 200:soup = BeautifulSoup(response.text, 'html.parser')title = soup.title.stringprint(f"页面标题: {title}")else:print(f"请求失败,状态码: {response.status_code}")if __name__ == "__main__":urls = ["https://www.example.com","https://www.example2.com","https://www.example3.com"]for url in urls:crawl_page(url)

在这段代码中,crawl_page函数负责发送 HTTP 请求,获取网页内容,并解析出网页的标题。在__main__部分,程序通过一个循环依次调用crawl_page函数,对每个 URL 进行爬取。这种单线程的执行方式意味着,只有当一个 URL 的爬取任务完全完成后,才会开始下一个 URL 的爬取。如果其中某个 URL 的响应时间较长,整个程序就会在这个 URL 上等待,导致后续的爬取任务无法及时进行,从而降低了整体的爬取效率。例如,当爬取一个网络延迟较高的网站时,单线程爬虫可能会在等待响应的过程中浪费大量时间,而这段时间内 CPU 资源却处于闲置状态,没有得到充分利用。

2.3 多线程爬虫的实现步骤

  1. 任务分解:将单线程爬虫中的爬取任务分解为多个子任务,每个子任务可以独立执行。例如,在上述单线程爬虫中,每个 URL 的爬取可以看作一个子任务,这些子任务之间相互独立,没有先后顺序的严格要求,可以同时进行。
  2. 线程创建:使用threading.Thread类创建多个线程,每个线程负责执行一个子任务。在创建线程时,需要指定线程要执行的函数(即子任务函数),以及传递给该函数的参数。例如:
import threadingthread1 = threading.Thread(target=crawl_page, args=("https://www.example.com",))
thread2 = threading.Thread(target=crawl_page, args=("https://www.example2.com",))
thread3 = threading.Thread(target=crawl_page, args=("https://www.example3.com",))

这里创建了三个线程,分别对应三个不同 URL 的爬取任务。

  1. 启动线程:调用线程的start()方法,启动各个线程,使其开始并发执行。一旦调用start()方法,线程就会进入就绪状态,等待 CPU 调度执行。例如:
thread1.start()
thread2.start()
thread3.start()

此时,三个线程会同时开始尝试获取 CPU 时间片,执行各自的爬取任务。

  1. 线程同步:使用join()方法等待所有线程执行完毕,确保程序在所有线程完成任务后再继续执行后续操作。join()方法会阻塞当前线程,直到被调用的线程执行结束。例如:
thread1.join()
thread2.join()
thread3.join()
print("所有线程执行完毕")

通过这种方式,可以保证在所有 URL 都爬取完成后,程序才会继续执行其他操作,避免了主线程提前结束而导致子线程未完成任务的情况。

2.4 代码示例与讲解

下面是将上述单线程爬虫改造成多线程爬虫的完整代码示例:

import requests
import threading
from bs4 import BeautifulSoupdef crawl_page(url):response = requests.get(url)if response.status_code == 200:soup = BeautifulSoup(response.text, 'html.parser')title = soup.title.stringprint(f"页面标题: {title}")else:print(f"请求失败,状态码: {response.status_code}")if __name__ == "__main__":urls = ["https://www.example.com","https://www.example2.com","https://www.example3.com"]threads = []for url in urls:thread = threading.Thread(target=crawl_page, args=(url,))threads.append(thread)thread.start()for thread in threads:thread.join()print("所有线程执行完毕")

在这段代码中:

  • 首先定义了crawl_page函数,其功能与单线程爬虫中的相同,负责爬取指定 URL 的网页并提取标题。
  • 在__main__部分,创建了一个空列表threads,用于存储线程对象。
  • 通过循环遍历urls列表,为每个 URL 创建一个新的线程。在创建线程时,使用threading.Thread类,指定target参数为crawl_page函数,表示该线程要执行的任务;args参数为一个元组,包含要传递给crawl_page函数的 URL 参数。创建好线程后,将其添加到threads列表中,并调用start()方法启动线程。
  • 最后,通过另一个循环遍历threads列表,对每个线程调用join()方法,等待所有线程执行完毕。当所有线程都完成任务后,打印出 “所有线程执行完毕” 的信息。这样,通过多线程的方式,大大提高了爬虫的执行效率,减少了整体的爬取时间。

三、应对挑战:解决资源竞争问题

3.1 资源竞争的产生原因

在多线程爬虫中,当多个线程同时访问和修改共享资源时,就容易产生资源竞争问题。以文件写入为例,假设有两个线程都要向同一个文件中写入数据。线程 A 获取到文件指针后,开始写入一段数据,但在它还未完全写完时,线程调度发生了变化,线程 B 获得了执行权,并且也获取到了文件指针。此时,线程 B 并不知道线程 A 还未完成写入操作,它也开始写入自己的数据,这就导致线程 A 和线程 B 写入的数据相互覆盖,最终文件中的数据变得混乱不堪,无法保证数据的完整性和正确性。

再比如在数据库操作中,多个线程同时对数据库中的同一记录进行修改。线程 A 读取了某条记录,准备对其某个字段进行更新,就在它执行更新操作之前,线程 B 也读取了同一条记录,并且由于线程调度,线程 B 先完成了对该记录的更新。然后线程 A 继续执行它的更新操作,此时线程 A 的更新操作是基于它之前读取到的旧数据进行的,这就导致线程 B 的更新被线程 A 覆盖,数据库中的数据出现错误,无法反映真实的更新情况。这种资源竞争问题在多线程爬虫中如果不加以解决,会严重影响爬虫的准确性和可靠性。

3.2 互斥锁(Mutex)的使用

  1. 原理介绍:互斥锁就像是一扇只能容纳一人通过的门,它确保在同一时刻只有一个线程能够访问共享资源。当一个线程获取到互斥锁后,就相当于它拿到了这扇门的钥匙,其他线程只能在门外等待,直到该线程释放互斥锁,也就是归还钥匙,其他线程才有机会获取锁并进入访问共享资源。通过这种方式,互斥锁有效地避免了多个线程同时访问共享资源导致的数据不一致问题。
  2. 代码示例
import threading# 模拟共享资源(这里用一个全局变量表示)
shared_data = []
lock = threading.Lock()def write_to_shared_data(data):global shared_data# 获取互斥锁lock.acquire()try:shared_data.append(data)print(f"线程 {threading.current_thread().name} 写入数据: {data}")finally:# 释放互斥锁lock.release()# 创建多个线程
threads = []
data_to_write = ["数据1", "数据2", "数据3"]
for i in range(len(data_to_write)):thread = threading.Thread(target=write_to_shared_data, args=(data_to_write[i],))threads.append(thread)thread.start()# 等待所有线程执行完毕
for thread in threads:thread.join()print("最终共享数据: ", shared_data)

在这段代码中,write_to_shared_data函数负责向共享资源shared_data中写入数据。在写入数据之前,先通过lock.acquire()获取互斥锁,确保同一时刻只有一个线程能够进入写入操作。写入完成后,使用lock.release()释放互斥锁,让其他线程有机会获取锁并进行写入。这样,无论有多少个线程同时尝试写入数据,都能保证数据的一致性,不会出现数据相互覆盖的情况。

3.3 信号量(Semaphore)的应用

  1. 原理介绍:信号量可以看作是一个允许多人同时通过的旋转门,它允许一定数量的线程同时访问共享资源。通过控制信号量的数量,我们可以限制并发访问的线程数。例如,将信号量的数量设置为 3,就意味着最多可以有 3 个线程同时通过这扇旋转门,访问共享资源。当有线程访问共享资源时,信号量的计数器会减 1;当线程访问结束后,信号量的计数器会加 1。当计数器为 0 时,表示没有可用的资源,其他线程需要等待,直到有线程释放资源,计数器增加。
  2. 代码示例
import threading
import time# 模拟共享资源(这里用一个简单的打印操作表示)
semaphore = threading.Semaphore(2)def access_shared_resource(thread_name):semaphore.acquire()try:print(f"线程 {thread_name} 进入共享资源区域")time.sleep(2)  # 模拟访问共享资源的操作print(f"线程 {thread_name} 离开共享资源区域")finally:semaphore.release()# 创建多个线程
threads = []
for i in range(5):thread = threading.Thread(target=access_shared_resource, args=(f"线程{i + 1}",))threads.append(thread)thread.start()# 等待所有线程执行完毕
for thread in threads:thread.join()

在这个示例中,semaphore = threading.Semaphore(2)创建了一个信号量,允许最多 2 个线程同时访问共享资源。每个线程在访问共享资源前,先通过semaphore.acquire()获取信号量,如果信号量的计数器大于 0,则获取成功,计数器减 1;如果计数器为 0,则线程会被阻塞,直到有其他线程释放信号量。线程访问结束后,通过semaphore.release()释放信号量,使计数器加 1,以便其他线程可以获取。通过这种方式,有效地控制了对共享资源的并发访问数量,避免了过多线程同时访问共享资源导致的资源竞争和性能问题。

3.4 队列(Queue)的运用

  1. 原理介绍:Python 的queue模块提供了线程安全的队列类,如Queue、LifoQueue等,这些队列类就像是一个安全的中转站,可用于在多线程之间安全地传递数据,避免资源竞争。以Queue为例,它是一个先进先出(FIFO)的队列,多个线程可以将数据放入队列中,也可以从队列中取出数据。由于Queue内部实现了线程同步机制,所以多个线程同时对队列进行操作时,不会出现数据不一致或错误的情况。当一个线程向队列中放入数据时,其他线程可以安全地等待并从队列中取出数据,从而实现线程间的解耦和数据传递。
  2. 代码示例
import threading
import queue
import requests
from bs4 import BeautifulSoup# 创建任务队列和结果队列
task_queue = queue.Queue()
result_queue = queue.Queue()def crawl(url):response = requests.get(url)if response.status_code == 200:soup = BeautifulSoup(response.text, 'html.parser')title = soup.title.stringresult_queue.put(title)else:result_queue.put(f"请求失败,状态码: {response.status_code}")def worker():while True:url = task_queue.get()if url is None:breakcrawl(url)task_queue.task_done()# 初始化任务队列
urls = ["https://www.example.com","https://www.example2.com","https://www.example3.com"
]
for url in urls:task_queue.put(url)# 创建多个线程
num_threads = 3
threads = []
for _ in range(num_threads):thread = threading.Thread(target=worker)thread.start()threads.append(thread)# 等待所有任务完成
task_queue.join()# 停止工作线程
for _ in range(num_threads):task_queue.put(None)# 等待所有线程执行完毕
for thread in threads:thread.join()# 获取并打印结果
while not result_queue.empty():result = result_queue.get()print(result)

在这段代码中,task_queue用于存储待爬取的 URL 任务,result_queue用于存储爬取的结果。worker函数从task_queue中获取 URL 任务,执行爬取操作,并将结果放入result_queue中。多个线程同时执行worker函数,通过task_queue和result_queue实现了线程间的任务分配和结果传递,避免了直接共享资源带来的竞争问题。task_queue.join()方法用于等待所有任务完成,result_queue.empty()方法用于判断结果队列中是否还有未处理的结果。通过这种方式,实现了多线程爬虫中线程间的安全协作和数据传递 。

四、数据说话:性能测试与分析

4.1 测试环境与方法

为了准确评估多线程爬虫相对于单线程爬虫的性能提升,我们搭建了如下测试环境:

  • 硬件环境:使用一台配备 Intel Core i7-10700K 处理器(8 核心 16 线程)、16GB DDR4 内存的计算机。
  • 软件环境:操作系统为 Windows 10 专业版,Python 版本为 3.9.7,相关依赖库包括requests 2.25.1、threading内置模块、time内置模块。

在测试方法上,我们选择爬取一个包含 100 个页面的小型网站。单线程爬虫按照顺序依次爬取每个页面,多线程爬虫则创建 5 个线程同时进行爬取。在代码中,使用time模块的time()函数记录爬虫开始和结束的时间,通过计算两者的差值来获取爬取所需的时间。例如:

import time
start_time = time.time()
# 单线程或多线程爬虫代码部分
end_time = time.time()
print(f"爬取时间: {end_time - start_time} 秒")

为了确保测试结果的准确性和可靠性,我们对单线程爬虫和多线程爬虫分别进行了 10 次测试,并取平均值作为最终的测试结果。

4.2 测试结果展示

经过多次测试,得到如下性能数据:

爬虫类型平均爬取时间(秒)数据吞吐量(KB/s)
单线程爬虫120.551.2
多线程爬虫35.8172.6

从表格中可以明显看出,多线程爬虫的爬取时间远远低于单线程爬虫,在本次测试中,多线程爬虫的爬取时间仅约为单线程爬虫的三分之一。在数据吞吐量方面,多线程爬虫也有显著提升,达到了单线程爬虫的 3 倍以上。

4.3 结果分析与总结

通过对比测试结果,可以清晰地看到多线程爬虫在提高爬取效率方面具有显著优势。多线程爬虫能够同时处理多个页面的请求,充分利用网络带宽和 CPU 资源,减少了等待时间,从而大大缩短了整体的爬取时间。在网络请求过程中,线程在等待服务器响应时处于空闲状态,此时多线程可以切换到其他线程执行任务,避免了 CPU 资源的浪费,提高了资源利用率。

然而,多线程爬虫在实际应用中也并非完美无缺。一方面,线程的创建和切换会带来一定的开销,当线程数量过多时,这种开销可能会抵消多线程带来的性能提升。例如,如果创建了大量的线程,每个线程都需要占用一定的内存空间,并且线程之间的切换需要保存和恢复线程上下文,这都会消耗额外的时间和资源。另一方面,Python 中的全局解释器锁(GIL)会限制多线程在 CPU 密集型任务中的并行执行能力。虽然爬虫主要是 I/O 密集型任务,但在某些情况下,如对爬取到的数据进行复杂的解析和处理时,GIL 可能会对性能产生一定的影响。所以,在使用多线程爬虫时,需要根据具体的任务和需求,合理调整线程数量,以达到最佳的性能表现。

五、总结与展望

通过本文的探讨,我们深入了解了多线程爬虫的实现及其关键要点。从单线程爬虫向多线程爬虫的转变,不仅是代码结构的调整,更是对爬虫效率的一次重大提升。通过threading模块,我们能够轻松创建和管理多个线程,实现任务的并发执行,大大缩短了数据爬取的时间。

在多线程爬虫的实践中,资源竞争问题是不可忽视的挑战。互斥锁、信号量和队列等工具为我们提供了有效的解决方案,确保在多线程环境下共享资源的安全访问,保障了数据的一致性和完整性。通过性能测试与分析,我们直观地看到多线程爬虫在爬取时间和数据吞吐量上相对于单线程爬虫的显著优势,同时也认识到线程数量的合理设置以及 GIL 等因素对性能的影响。

在未来的项目中,读者可以根据具体的需求和场景,灵活运用多线程爬虫技术。对于大规模数据的爬取任务,多线程爬虫能够显著提高效率,满足业务对数据获取速度的要求。同时,随着技术的不断发展,还可以进一步探索与异步 I/O、分布式爬虫等技术的结合,以应对更复杂的爬取需求,不断拓展爬虫技术的应用边界,为数据驱动的决策和分析提供更强大的支持。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SQL实验】触发器

下载素材文件”tsgl”、“成绩管理”,将tsgl.bak和成绩管理.bak数据库还原到库中【导入操作在之前的文章中详细讲过】 触发器 1、为图书表设置更新触发器,根据总编号来更新书名、作者、出版社、分类号和单价(根据总编号找到相应记录,然后更新书名、作者…

Win10系统Docker+DeepSeek+ragflow搭建本地知识库

文章目录 1、安装ollama1.1 下载1.2 安装1.3 cmd命令行测试安装成功1.4 拉取模型2、安装ragflow2.1 下载项目2.2 通过docker拉取镜像安装2.3 查看docker日志是否安装成功3、模型配置3.1 第一次登录需要注册3.2 模型添加4、知识库配置4.1 创建知识库4.2 上传文档4.3 解析5、聊天…

redis的应用,缓存,分布式锁

1.应用 1.1可以用作缓存 作用:提交数据的查询效率,减少对数据库的访问频率 什么数据适合放入缓存 1.查询频率高,修改频率低 2.对安全系数比较低 如何实现 Service public class DeptServer {Autowiredprivate DeptMapper deptMapper;Auto…

springboot整合 xxl-job

文章目录 一、xxl-job是什么二、使用步骤 1. 下载并运行管理端代码2. 访问管理页面,确认是否启动成功3. 配置执行器【在自己的springboot项目中配置】4. 在页面上创建执行器和任务,与项目中绑定 总结参考 一、xxl-job是什么 XXL-JOB 是一个分布式任务调…

Jenkins 环境搭建---基于 Docker

前期准备 提前安装jdk、maven、nodeJs(如果需要的话) 创建 jenkins 环境目录,用来当做挂载卷 /data/jenkins/ 一:拉取 Jenkins 镜像 docker pull jenkins/jenkins:lts 二:设置 Jenkins挂载目录 mkdir -p ~/jen…

小米路由器 AX3000T 降级后无法正常使用,解决办法

问题描述 买了个 AX3000T 路由器,想安装 OpenWRT 或者 安装 Clash 使用,看教程说是需要降级到 v1.0.47 版本。 结果刷机之后路由器无法打开了,一直黄灯亮,中间灭一下,又是黄灯长亮,没有 WIFI 没有连接。以…

金融学-金融机构

前言 金融机构在金融体系运行体系运营中起着不可获缺的关键作用.如规则的制定与监管-中央银行,体系的运营证券公司,体系的供贷的参与者金融中介.本章将用一种说明我们的金融体系是怎样改进经济效率的经济分析,来讲述相关金融机构 金融结构的经济学分析 世界各国的金融体系在…

公网远程家里局域网电脑过程详细记录,包含设置路由器。

由于从校内迁居小区,校内需要远程控制访问小区内个人电脑,于是早些时间刚好自己是电信宽带,可以申请公网ipv4不需要花钱,所以就打电话直接申请即可,申请成功后访问光猫设备管理界面192.168.1.1,输入用户名密码登录超管(密码是网上查下就有了)设置了光猫为桥接模式,然后…

002 SpringCloudAlibaba整合 - Feign远程调用、Loadbalancer负载均衡

前文地址: 001 SpringCloudAlibaba整合 - Nacos注册配置中心、Sentinel流控、Zipkin链路追踪、Admin监控 文章目录 8.Feign远程调用、loadbalancer负载均衡整合1.OpenFeign整合1.引入依赖2.启动类添加EnableFeignClients注解3.yml配置4.日志配置5.远程调用测试6.服务…

基于javaweb的SpringBoot校园二手商品系统设计和实现(源码+文档+部署讲解)

技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

国产开源PDF解析工具MinerU

前言 PDF的数据解析是一件较困难的事情,几乎所有商家都把PDF转WORD功能做成付费产品。 PDF是基于PostScript子集渲染的,PostScript是一门图灵完备的语言。而WORD需要的渲染,本质上是PDF能力的子集。大模型领域,我们的目标文件格…

stm32单片机个人学习笔记16(SPI通信协议)

前言 本篇文章属于stm32单片机(以下简称单片机)的学习笔记,来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。 STM32入门教程-2023版 细…

Springboot + Ollama + IDEA + DeepSeek 搭建本地deepseek简单调用示例

1. 版本说明 springboot 版本 3.3.8 Java 版本 17 spring-ai 版本 1.0.0-M5 deepseek 模型 deepseek-r1:7b 需要注意一下Ollama的使用版本: 2. springboot项目搭建 可以集成在自己的项目里,也可以到 spring.io 生成一个项目 生成的话,如下…

Ubuntu 的RabbitMQ安装

目录 1.安装Erlang 查看erlang版本 退出命令 2. 安装 RabbitMQ 3.确认安装结果 4.安装RabbitMQ管理界面 5.启动服务并访问 1.启动服务 2.查看服务状态 3.通过IP:port 访问界面 4.添加管理员用户 a)添加用户名:admin,密码&#xff1…

Powershell Install deepseek

前言 deepseekAI助手。它具有聊天机器人功能,可以与用户进行自然语言交互,回答问题、提供建议和帮助解决问题。DeepSeek 的特点包括: 强大的语言理解能力:能够理解和生成自然语言,与用户进行流畅的对话。多领域知识&…

VS Code 如何搭建C/C++开发环境

目录 1.VS Code是什么 2. VS Code的下载和安装 2.1 下载和安装 2.2.1 下载 2.2.2 安装 2.2 环境的介绍 2.3 安装中文插件 3. VS Code配置C/C开发环境 3.1 下载和配置MinGW-w64编译器套件 3.1.1 下载 3.1.2 配置 3.2 安装C/C插件 3.3 重启VSCode 4. 在VSCode上编写…

vue从入门到精通(十一):条件渲染

条件渲染 1.v-if 写法: (1).v-if“表达式” (2).v-else-if“表达式” (3).v-else“表达式” 适用于:切换频率较低的场景。 特点:不展示的DOM元素直接被移除。 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断” 2.v-show 写法:v-show“…

Java之——“String类”(内容较多,结合目录察看分类)

前言 在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面向对象的思想,而字符串应用又…

【C++篇】树影摇曳,旋转无声:探寻AVL树的平衡之道

文章目录 从结构到操作:手撕AVL树的实现一、AVL树介绍1.1 什么是AVL树1.2 平衡因子的定义1.3 平衡的意义1.4 AVL树的操作 二、AVL树的节点结构2.1 节点结构的定义: 三、插入操作3.1 插入操作概述3.2 步骤1:按二叉查找树规则插入节点3.3 步骤2…

限制Doris端口访问,解决REST API漏洞

方案一:通过Linux防火墙规则限制 目标:限制Doris的端口,只允许指定的ip访问此端口,其他禁止 1、设置规则 1.1、准备工作 注意:以上命令顺序不能错,先禁止后允许,另外此处只是临时设置。 # …