python爬虫学习(三十三天)---多线程上篇

hello,小伙伴们!我是喔的嘛呀。今天我们来学习多线程方面的知识。

目录

一、了解多线程

(1)大概描述

(2)多线程爬虫的优势

(3)多线程爬虫的实现方式

(4)多线程爬虫的注意事项

二、Python中的多线程实现

1. 基本概念

2. 线程创建与启动

示例代码:

3. 线程同步与互斥

示例代码:使用锁(Lock)

4. 线程池

示例代码:使用线程池

5. 总结

三、 线程安全和数据共享

线程安全

示例代码:使用锁确保线程安全

数据共享

示例代码:线程间共享数据


一、了解多线程

(1)大概描述

多线程爬虫是一种使用多个线程同时执行网页爬取任务的爬虫技术。在传统的单线程爬虫中,爬虫会按照顺序逐个访问URL并下载网页内容,这种方式在处理大量URL或网络延迟较高时效率较低。而多线程爬虫则通过创建多个线程,每个线程独立地访问和下载网页,从而显著提高爬取效率。

(2)多线程爬虫的优势

  1. 提高爬取效率:多线程可以并行处理多个任务,使得爬虫能够同时访问和下载多个网页,从而显著缩短整体爬取时间。
  2. 充分利用资源:多线程能够充分利用计算机的多核处理器资源,使得爬虫在处理大规模爬取任务时更加高效。
  3. 减少网络延迟影响:由于网络延迟的存在,单线程爬虫在访问每个URL时都需要等待网络响应。而多线程爬虫可以同时处理多个URL,从而减少等待时间,降低网络延迟对爬取效率的影响。
  4. 实现复杂的爬取策略:多线程爬虫可以方便地实现复杂的爬取策略,如分布式爬取、优先级调度等。通过合理分配线程资源,可以实现对不同网页的优先爬取或并行爬取。

(3)多线程爬虫的实现方式

在Python中,可以使用threading模块来实现多线程爬虫。通常的做法是创建一个线程池,然后向线程池中提交任务(即访问和下载网页的任务)。线程池中的线程会并行执行这些任务,并返回结果。为了避免线程间的竞争和冲突,需要使用线程安全的队列(如queue.Queue)来管理待爬取的URL和已爬取的数据。

(4)多线程爬虫的注意事项

  1. 线程安全:多线程编程时需要注意线程安全,避免多个线程同时访问和修改共享资源导致的数据混乱和错误。可以通过使用锁(如threading.Lock)或其他同步机制来保证线程安全。
  2. 资源消耗:多线程会消耗更多的系统资源(如CPU、内存等),因此需要合理控制线程数量,避免过度消耗资源导致系统崩溃或性能下降。
  3. 网络爬虫法规:在编写多线程爬虫时需要遵守相关法律法规和网站的使用协议,不得进行恶意爬取、数据滥用等行为。
  4. 异常处理:多线程爬虫在执行过程中可能会遇到各种异常情况(如网络错误、页面结构变化等),需要编写健壮的异常处理代码来确保程序的稳定性和可靠性。

二、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密集型任务或外部资源的访问(如文件、网络请求等),多线程仍然可以提高程序的效率。然而,即使在这些情况下,我们也需要确保线程安全和数据共享的正确性。

线程安全

线程安全通常通过以下几种方式实现:

  1. 避免共享状态:尽量让线程只操作自己的局部变量,避免共享状态。
  2. 使用线程同步机制:如锁(Lock)、条件变量(Condition)、信号量(Semaphore)等。
  3. 使用线程安全的数据结构: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,但它应该是一个接近这个值的数。

好了,今天我们先学习多进程爬虫的三个知识点,明天我们将会学习后三个。再见啦,兄弟姐妹们。

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

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

相关文章

头歌资源库(25)地图着色

一、 问题描述 任何平面区域图都可以用四种颜色着色,使相邻区域颜色互异。这就是四色定理。要求给定区域图,排出全部可能的着色方案。例如,区域图如下图所示: 要求用四种颜色着色。 则输入: 10 4 (分别表示…

【ZhangQian AI模型部署】目标检测、SAM、3D目标检测、旋转目标检测、人脸检测、检测分割、关键点、分割、深度估计、车牌识别、车道线识别

在模型部署落地(主要部署到rk3588)折腾了这么多年,把这些年折腾过的模型整理了一下,所有的流程说明、代码模型都完全开放的,欢迎交流学习。有的是为了项目、有的是为了学习、还有的是为了找点事做、有的完全是为了安抚…

字节码编程bytebuddy之实现抽象类并并添加自定义注解

写在前面 本文看下使用bytebuddy如何实现抽象类,并在子类中添加自定义注解。 1:代码 1.1:准备基础代码 类和方法注解 package com.dahuyou.bytebuddy.cc.mine;import java.lang.annotation.ElementType; import java.lang.annotation.Re…

Open3D 提取点云的重叠区域和非重叠区域

目录 一、概述 二、代码实现 三、实现效果 3.1原始点云 3.2处理后点云 一、概述 在点云处理中,提取点云的重叠区域和非重叠区域可以提供有价值的信息,用于多种应用。以下是详细解释及其作用: 配准质量评估:在多视角点云拼接或配准过程中,通过分析重叠区域,可…

python-27-零基础自学python

学习内容:《python编程:从入门到实践》第二版 知识点: 统计文本单词数、 解决问题: gbk codec cant decode byte 0x9d in position 995: illegal multibyte sequence” 练习内容: 练习10-10:常见单词 …

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第一篇 嵌入式Linux入门篇-第二十六章 安装超级终端软件

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

短视频矩阵系统是什么?怎么搭建短视频矩阵系统?一文了解矩阵模式

在数字时代,短视频已成为信息传播的新宠,而短视频矩阵系统则是品牌和个人在短视频领域取得突破的重要工具。那么,短视频矩阵系统究竟是什么?如何搭建这样一个高效的系统?它又能够解决哪些问题呢?本文将为您…

Memcached客户端库对决:解锁高性能缓存的钥匙

标题:Memcached客户端库对决:解锁高性能缓存的钥匙 Memcached作为一个高效的分布式内存缓存系统,在提升应用性能和扩展性方面发挥着关键作用。为了与Memcached服务器进行交互,开发者可以使用多种客户端库。这些库提供了丰富的API…

Spring中事件监听器

实现ApplicationListener接口 Configuration public class A48 {public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(A48.class);context.getBean(MyService.class).doBusiness();context.close()…

C#绘制阻抗圆图初步

阻抗圆图,或者叫史密斯图,是无线电设计方面用的; 基本的阻抗圆图如下, 下面尝试用C#能不能画一下; 先在网上找一个画坐标的C#类,它的效果如下; 自己再增加一个函数,可以绘制中心在…

地址翻译过程(TLB-->页表-->Cache-->主存-->外存)

目录 1.地址结构 2.查找快表或页表:从虚拟地址--->物理地址 3.通过物理地址访问数据 设某系统满足以下条件: •有一个TLB与一个data Cache •存储器以字节为编址单位 •虚拟地址 14位 •物理地址 12位 •页面大小为 64B •TLB 为四路组相联&#x…

Windows 10怎麼系統中修改IP地址-okeyproxy

本文將詳細介紹在Windows 10系統中修改IP地址的步驟,並提供一些創新性的技巧,幫助大家更好地管理網路設置。 IP地址分為動態IP和靜態IP兩種。動態IP由路由器或ISP(互聯網服務提供商)自動分配,靜態IP則是手動設置的固定…

消防灭火设备软体水枪的使用方法

软体水枪是一款专为消防、应急救援等领域设计的灭火工具。其内胆采用大容量设计,能够储存足够的灭火剂,满足长时间、大范围的灭火需求。软体水枪是一种高效、便捷的灭火工具。它利用压力和流体力学原理,通过扣动扳机将储水箱中的水以高速喷射…

tp计算距离,筛选0-10km距离内商家

$field_lat lat;// 数据库字段名 - 纬度 -90到90 $field_lng lng;// 数据库字段名 - 经度 -180到180 $lat $params[lat];// 数据库字段名 - 纬度 -90到90 $lng $params[lng];// 数据库字段名 - 经度 -180到180 //语句 $field "(6378.138 …

【数据结构】笔试面试中常用的数据结构操作

笔试面试中常用的数据结构操作 排序与查找 请编写一个C程序&#xff0c;实现冒泡排序。 #include <stdio.h> // 冒泡排序函数 void bubbleSort(int arr[], int n) {for (int i 0; i < n - 1; i) {for (int j 0; j < n - i - 1; j) {if (arr[j] > arr[j 1]…

《金山 WPS AI 2.0:重塑办公未来的智能引擎》

AITOP100平台获悉&#xff0c;在 2024 世界人工智能大会这一科技盛宴上&#xff0c;金山办公以其前瞻性的视野和创新的技术&#xff0c;正式发布了 WPS AI 2.0&#xff0c;犹如一颗璀璨的星辰&#xff0c;照亮了智能办公的新征程&#xff0c;同时首次公开的金山政务办公模型 1.…

【python学习】将两个 数组连接起来

在Python中&#xff0c;将两个数组&#xff08;或列表&#xff09;连接起来非常简单。对于Python的原生列表&#xff0c;你可以使用加号()操作符或者extend()方法。对于NumPy数组&#xff0c;你可以使用numpy.concatenate()函数或numpy.append()函数&#xff08;尽管numpy.appe…

Spring AOP 使用方式

ProxyFactory代理工厂提供了基于切面构造代理对象的能力&#xff0c;Spring框架结合IOC对此进行了一层封装以适应多种场景。封装后为用户提供了一套Spring风格的“API”(使用方式)。 1 xml配置方式 引入AOP的schema: <beans xmlns"http://www.springframework.org/sche…

PyTorch | 加速模型训练的妙招

引言 提升机器学习模型的训练速度是每位机器学习工程师的共同追求。训练速度的提升意味着实验周期的缩短&#xff0c;进而加速产品的迭代过程。同时&#xff0c;这也表示在进行单一模型训练时&#xff0c;所需的资源将会减少。简而言之&#xff0c;我们追求的是效率。 熟悉 PyT…

padStart方法用来格式化数据

在别人写的代码中看到的padStart方法&#xff0c;没用过&#xff0c;浅浅记录一下~ padStart方法的使用 padStart是 字符串类型的方法&#xff0c;可以用来格式化字符串&#xff0c;在字符串添加指定的字符以达到指定的长度&#xff0c;例如&#xff1a;可以用来格式化日期 …