python爬虫学习(三十三天)---多线程上篇
hello,小伙伴们!我是喔的嘛呀。今天我们来学习多线程方面的知识。
目录
一、了解多线程
(1)大概描述
(2)多线程爬虫的优势
(3)多线程爬虫的实现方式
(4)多线程爬虫的注意事项
二、Python中的多线程实现
1. 基本概念
2. 线程创建与启动
示例代码:
3. 线程同步与互斥
示例代码:使用锁(Lock)
4. 线程池
示例代码:使用线程池
5. 总结
三、 线程安全和数据共享
线程安全
示例代码:使用锁确保线程安全
数据共享
示例代码:线程间共享数据
一、了解多线程
(1)大概描述
多线程爬虫是一种使用多个线程同时执行网页爬取任务的爬虫技术。在传统的单线程爬虫中,爬虫会按照顺序逐个访问URL并下载网页内容,这种方式在处理大量URL或网络延迟较高时效率较低。而多线程爬虫则通过创建多个线程,每个线程独立地访问和下载网页,从而显著提高爬取效率。
(2)多线程爬虫的优势
- 提高爬取效率:多线程可以并行处理多个任务,使得爬虫能够同时访问和下载多个网页,从而显著缩短整体爬取时间。
- 充分利用资源:多线程能够充分利用计算机的多核处理器资源,使得爬虫在处理大规模爬取任务时更加高效。
- 减少网络延迟影响:由于网络延迟的存在,单线程爬虫在访问每个URL时都需要等待网络响应。而多线程爬虫可以同时处理多个URL,从而减少等待时间,降低网络延迟对爬取效率的影响。
- 实现复杂的爬取策略:多线程爬虫可以方便地实现复杂的爬取策略,如分布式爬取、优先级调度等。通过合理分配线程资源,可以实现对不同网页的优先爬取或并行爬取。
(3)多线程爬虫的实现方式
在Python中,可以使用threading
模块来实现多线程爬虫。通常的做法是创建一个线程池,然后向线程池中提交任务(即访问和下载网页的任务)。线程池中的线程会并行执行这些任务,并返回结果。为了避免线程间的竞争和冲突,需要使用线程安全的队列(如queue.Queue
)来管理待爬取的URL和已爬取的数据。
(4)多线程爬虫的注意事项
- 线程安全:多线程编程时需要注意线程安全,避免多个线程同时访问和修改共享资源导致的数据混乱和错误。可以通过使用锁(如
threading.Lock
)或其他同步机制来保证线程安全。 - 资源消耗:多线程会消耗更多的系统资源(如CPU、内存等),因此需要合理控制线程数量,避免过度消耗资源导致系统崩溃或性能下降。
- 网络爬虫法规:在编写多线程爬虫时需要遵守相关法律法规和网站的使用协议,不得进行恶意爬取、数据滥用等行为。
- 异常处理:多线程爬虫在执行过程中可能会遇到各种异常情况(如网络错误、页面结构变化等),需要编写健壮的异常处理代码来确保程序的稳定性和可靠性。
二、Python中的多线程实现
在Python中,多线程的实现主要依赖于threading
模块。下面将详细介绍Python中多线程的实现,并附带示例代码。
1. 基本概念
在Python中,线程是程序执行的最小单元。多线程允许在单个程序中并发地执行多个线程。由于Python的全局解释器锁(GIL),多线程在CPU密集型任务上的并行执行效果并不明显,但在I/O密集型任务(如网络请求)中,多线程可以显著提高性能。
2. 线程创建与启动
在Python中,可以通过继承threading.Thread
类并重写run
方法来创建一个线程,然后调用start
方法启动线程。
示例代码:
import threading
import time def worker(name): """线程执行的函数""" print(f"线程 {name} 开始工作") time.sleep(2) # 模拟耗时操作 print(f"线程 {name} 工作完成") # 继承 threading.Thread 类
class MyThread(threading.Thread): def __init__(self, name): super().__init__() # 调用父类构造函数 self.name = name def run(self): """线程运行的函数""" worker(self.name) # 调用上面定义的函数 # 创建并启动线程
threads = []
for i in range(5): t = MyThread(f"Thread-{i}") t.start() # 启动线程 threads.append(t) # 等待所有线程完成
for t in threads: t.join() print("所有线程已完成")
3. 线程同步与互斥
由于多线程并发执行,可能会存在对共享资源的竞争访问问题。Python提供了多种同步机制来解决这个问题,如锁(Lock)、条件变量(Condition)、信号量(Semaphore)等。
示例代码:使用锁(Lock)
import threading class Counter: def __init__(self): self.lock = threading.Lock() self.value = 0 def increment(self): with self.lock: # 使用 with 语句自动管理锁的获取和释放 self.value += 1 # 创建多个线程,每个线程都调用 Counter 的 increment 方法
threads = []
counter = Counter()
for i in range(1000): t = threading.Thread(target=counter.increment) t.start() threads.append(t) # 等待所有线程完成
for t in threads: t.join() print(f"最终计数值:{counter.value}") # 应该输出 1000
4. 线程池
为了避免创建过多线程导致的系统资源耗尽问题,可以使用线程池来限制同时运行的线程数量。Python的concurrent.futures
模块提供了ThreadPoolExecutor
类来实现线程池。
示例代码:使用线程池
import concurrent.futures
import time def worker(n): print(f"开始工作:{n}") time.sleep(2) # 模拟耗时操作 return f"结果:{n}" # 创建一个线程池,最大线程数为 5
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: # 提交多个任务到线程池执行 futures = [executor.submit(worker, i) for i in range(10)] # 等待所有任务完成,并获取结果 for future in concurrent.futures.as_completed(futures): try: result = future.result() print(result) except Exception as e: print(f"生成了一个异常:{e}") print("所有任务已完成")
5. 总结
Python中的多线程实现主要依赖于threading
模块,通过继承threading.Thread
类并重写run
方法可以创建自定义的线程。同时,Python也提供了多种同步机制来解决多线程并发执行时可能存在的共享资源竞争访问问题。为了避免创建过多线程导致的系统资源耗尽问题,可以使用线程池来限制同时运行的线程数量。
三、 线程安全和数据共享
线程安全和数据共享是在多线程编程中必须考虑的两个重要概念。线程安全指的是在多线程环境下,代码的执行结果总是符合预期,不会因为线程间的并发执行而导致数据不一致或其他不可预期的结果。数据共享则是指多个线程访问和操作同一块内存区域的数据。
在Python中,由于全局解释器锁(GIL)的存在,CPU密集型任务在多线程中并不会真正并行执行,但对于I/O密集型任务或外部资源的访问(如文件、网络请求等),多线程仍然可以提高程序的效率。然而,即使在这些情况下,我们也需要确保线程安全和数据共享的正确性。
线程安全
线程安全通常通过以下几种方式实现:
- 避免共享状态:尽量让线程只操作自己的局部变量,避免共享状态。
- 使用线程同步机制:如锁(Lock)、条件变量(Condition)、信号量(Semaphore)等。
- 使用线程安全的数据结构:Python标准库中的
queue
模块提供了线程安全的队列实现。
示例代码:使用锁确保线程安全
import threading class SharedCounter: def __init__(self): self.lock = threading.Lock() self.value = 0 def increment(self): with self.lock: self.value += 1 def decrement(self): with self.lock: self.value -= 1 def get_value(self): # 这里没有修改值,所以不需要加锁 return self.value # 创建多个线程对SharedCounter进行操作
def worker(counter, action, n): for _ in range(n): if action == 'inc': counter.increment() elif action == 'dec': counter.decrement() threads = []
counter = SharedCounter() # 创建增加线程
for _ in range(10): t = threading.Thread(target=worker, args=(counter, 'inc', 1000)) threads.append(t) t.start() # 创建减少线程(为了演示,这里只创建一个)
t = threading.Thread(target=worker, args=(counter, 'dec', 500))
threads.append(t)
t.start() # 等待所有线程完成
for t in threads: t.join() # 输出最终值,应该接近5000(10个线程各增加1000次,1个线程减少500次)
print(counter.get_value())
数据共享
在Python中,由于所有对象都是通过引用传递的,因此数据共享是隐式的。但是,当多个线程需要访问和修改同一块数据时,就需要考虑线程同步和数据一致性的问题。
示例代码:线程间共享数据
import threading # 共享的数据
shared_data = {'count': 0}
lock = threading.Lock() def increment_data(): global shared_data for _ in range(100000): with lock: shared_data['count'] += 1 def decrement_data(): global shared_data for _ in range(50000): with lock: shared_data['count'] -= 1 # 创建线程
t1 = threading.Thread(target=increment_data)
t2 = threading.Thread(target=decrement_data) # 启动线程
t1.start()
t2.start() # 等待线程完成
t1.join()
t2.join() # 输出最终结果,由于有线程竞争和同步,结果可能不是精确的50000
print(shared_data['count'])
在这个例子中,我们使用了全局变量shared_data
来模拟共享数据,并使用锁来确保在修改shared_data
时的线程安全。需要注意的是,即使使用了锁,由于线程调度的不确定性,最终的结果可能不是精确的50000,但它应该是一个接近这个值的数。
好了,今天我们先学习多进程爬虫的三个知识点,明天我们将会学习后三个。再见啦,兄弟姐妹们。
相关文章:
python爬虫学习(三十三天)---多线程上篇
hello,小伙伴们!我是喔的嘛呀。今天我们来学习多线程方面的知识。 目录 一、了解多线程 (1)大概描述 (2)多线程爬虫的优势 (3)多线程爬虫的实现方式 (4)…...

JavaScript 原型链那些事
在讲原型之前我们先来了解一下函数。 在JS中,函数的本质就是对象,它与其他对象不同的是,创建它的构造函数与创建其他对象的构造函数不一样。那产生函数对象的构造函数是什么呢?是一个叫做Function的特殊函数,通过newFu…...

nginx的知识面试易考点
Nginx概念 Nginx 是一个高性能的 HTTP 和反向代理服务。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好。 Nginx 专为性能优化而开发,性能是其最重要的考量指标,实现上非常注重效率&#…...

每日Attention学习9——Efficient Channel Attention
模块出处 [CVPR 20] [link] [code] ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks 模块名称 Efficient Channel Attention (ECA) 模块作用 通道注意力 模块结构 模块代码 import torch import torch.nn as nn import torch.nn.functional …...

Java语言程序设计——篇三(1)
选择结构 概述选择单分支if语句例题讲解 双分支if-else语句例题讲解 条件运算符多分支的if-else语句例题讲解 嵌套的if语句例题讲解 switch语句结构例题讲解代码演示运行结果 概述 Java中的控制结构,包括: 1、选择结构( if、if-else、switch ) 2、循环结…...

基于SpringBoot实现轻量级的动态定时任务调度
在使用SpringBoot框架进行开发时,一般都是通过Scheduled注解进行定时任务的开发: Component public class TestTask {Scheduled(cron"0/5 * * * * ? ") //每5秒执行一次public void execute(){SimpleDateFormat df new SimpleDateFormat(…...

夸克升级“超级搜索框” 推出AI搜索为中心的一站式AI服务
大模型时代,生成式AI如何革新搜索产品?阿里智能信息事业群旗下夸克“举手答题”。7月10日,夸克升级“超级搜索框”,推出以AI搜索为中心的一站式AI服务,为用户提供从检索、创作、总结,到编辑、存储、分享的一…...

element-ui el-select选择器组件下拉框增加自定义按钮
element-ui el-select选择器组件下拉框增加自定义按钮 先看效果 原理:在el-select下添加禁用的el-option,将其value绑定为undefined,然后覆盖el-option禁用状态下的默认样式即可 示例代码如下: <template><div class…...

Python基于you-get下载网页上的视频
1.python 下载地址 下载 : https://www.python.org/downloads/ 2. 配置环境变量 配置 python_home 地址 配置 python_scripts 地址 在path 中加入对应配置 3. 验证 C:\Users>python --version Python 3.12.4C:\Users>wheel version wheel 0.43.04. 下载 c…...
大模型/NLP/算法面试题总结3——BERT和T5的区别?
1、BERT和T5的区别? BERT和T5是两种著名的自然语言处理(NLP)模型,它们在架构、训练方法和应用场景上有一些显著的区别。以下是对这两种模型的详细比较: 架构 BERT(Bidirectional Encoder Representation…...
vue3项目打包的时候,怎么区别测试环境,和本地环境
在Vue 3项目中区别测试环境和本地环境,并标记接口的方法可以通过环境变量来实现。 首先,你可以在你的项目根目录下创建一个.env文件,并定义你的环境变量。比如,你可以创建.env.local作为本地环境的配置文件,.env.test…...
小特性 大用途 —— YashanDB JDBC驱动的这些特性你都get了吗?
在现代数据库应用场景中,系统的高可用性和负载均衡是确保服务稳定性的基石。YashanDB JDBC驱动通过其创新的多IP配置特性,为用户带来了简洁而强大的解决方案,以实现数据库连接的高可用性和负载均衡,满足企业级应用的高要求。 01 …...
全网最全的软件测试面试八股文
前面看到了一些面试题,总感觉会用得到,但是看一遍又记不住,所以我把面试题都整合在一起,都是来自各路大佬的分享,为了方便以后自己需要的时候刷一刷,不用再到处找题,今天把自己整理的这些面试题…...

VMware虚拟机配置桥接网络
转载:虚拟机桥接网络配置 一、VMware三种网络连接方式 VMware提供了三种网络连接方式,VMnet0, VMnet1, Vmnet8,分别代表桥接,Host-only及NAT模式。在VMware的编辑-虚拟网络编辑器可看到对应三种连接方式的设置(如下图…...
华为机考真题 -- 攀登者1
题目描述: 攀登者喜欢寻找各种地图,并且尝试攀登到最高的山峰。地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素0代表地面。 一个山脉可能有多座山峰(山峰定义:高度大于相邻位置的高度,或在地图边界且高度大于相邻的高度)。登山者…...

深入理解Python密码学:使用PyCrypto库进行加密和解密
深入理解Python密码学:使用PyCrypto库进行加密和解密 引言 在现代计算领域,信息安全逐渐成为焦点话题。密码学,作为信息保护的关键技术之一,允许我们加密(保密)和解密(解密)数据。P…...
MMSegmentation笔记
如何训练自制数据集? 首先需要在 mmsegmentation/mmseg/datasets 目录下创建一个自制数据集的配置文件,以我的苹果叶片病害分割数据集为例,创建了mmsegmentation/mmseg/datasets/appleleafseg.py 可以看到,这个配置文件主要定义…...

Python基础语法:变量和数据类型详解(整数、浮点数、字符串、布尔值)①
文章目录 变量和数据类型详解(整数、浮点数、字符串、布尔值)一、变量二、数据类型1. 整数(int)2. 浮点数(float)3. 字符串(str)4. 布尔值(bool) 三、类型转换…...

【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
目录 1 -> 红黑树 1.1 -> 红黑树的概念 1.2 -> 红黑树的性质 1.3 -> 红黑树节点的定义 1.4 -> 红黑树的结构 1.5 -> 红黑树的插入操作 1.6 -> 红黑树的验证 1.8 -> 红黑树与AVL树的比较 2 -> 红黑树模拟实现STL中的map与set 2.1 -> 红…...

MySQL DDL
数据库 1 创建数据库 CREATE DATABASE 数据库名 CREATE DATABASE IF NOT EXISTS 数据库名;(判断是否存在) CREATE DATABASE 数据库名 CHARACTER SET 字符 2 查看数据库 SHOW DATABASES; 查看某个数据库的信息 SHOW CAEATE DATABASE 数据库名 3 修改数据库 …...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...